From d3550dfcd64cfc182fd120338d3e7eb25ac339af Mon Sep 17 00:00:00 2001
From: Todd Stinson <stinson@lindenlab.com>
Date: Mon, 19 Mar 2012 16:24:51 -0700
Subject: [PATCH] Adding non-volume status to the pathfinding linkset to
 restrict non-convex linksets from being set to be a material or exclusion
 volume.

---
 .../newview/llfloaterpathfindinglinksets.cpp  | 209 ++++++++++--------
 indra/newview/llfloaterpathfindinglinksets.h  |  19 +-
 indra/newview/llpathfindinglinkset.cpp        |  99 +++++----
 indra/newview/llpathfindinglinkset.h          |  20 +-
 .../xui/en/floater_pathfinding_linksets.xml   |   6 +-
 .../skins/default/xui/en/notifications.xml    |  27 +++
 6 files changed, 223 insertions(+), 157 deletions(-)

diff --git a/indra/newview/llfloaterpathfindinglinksets.cpp b/indra/newview/llfloaterpathfindinglinksets.cpp
index af2024021b5..42a8f4e9510 100644
--- a/indra/newview/llfloaterpathfindinglinksets.cpp
+++ b/indra/newview/llfloaterpathfindinglinksets.cpp
@@ -259,11 +259,10 @@ void LLFloaterPathfindingLinksets::draw()
 			{
 				const LLScrollListItem *selectedItem = *selectedItemIter;
 
-				const std::string &objectName = selectedItem->getColumn(0)->getValue().asString();
-
 				LLViewerObject *viewerObject = gObjectList.findObject(selectedItem->getUUID());
 				if (viewerObject != NULL)
 				{
+					const std::string &objectName = selectedItem->getColumn(0)->getValue().asString();
 					gObjectList.addDebugBeacon(viewerObject->getPositionAgent(), objectName, LLColor4(0.f, 0.f, 1.f, 0.8f), LLColor4(1.f, 1.f, 1.f, 1.f), 6);
 				}
 			}
@@ -627,45 +626,7 @@ void LLFloaterPathfindingLinksets::updateEditFieldValues()
 		mEditB->setValue(LLSD(linksetPtr->getWalkabilityCoefficientB()));
 		mEditC->setValue(LLSD(linksetPtr->getWalkabilityCoefficientC()));
 		mEditD->setValue(LLSD(linksetPtr->getWalkabilityCoefficientD()));
-
-		switch (getAllowLinksetUse())
-		{
-		case kAllowLinksetUseAll :
-			mEditLinksetUseWalkable->setEnabled(TRUE);
-			mEditLinksetUseStaticObstacle->setEnabled(TRUE);
-			mEditLinksetUseDynamicObstacle->setEnabled(TRUE);
-			mEditLinksetUseMaterialVolume->setEnabled(TRUE);
-			mEditLinksetUseExclusionVolume->setEnabled(TRUE);
-			mEditLinksetUseDynamicPhantom->setEnabled(TRUE);
-			break;
-		case kAllowLinksetUseOnlyNonPhantom :
-			mEditLinksetUseWalkable->setEnabled(TRUE);
-			mEditLinksetUseStaticObstacle->setEnabled(TRUE);
-			mEditLinksetUseDynamicObstacle->setEnabled(TRUE);
-			mEditLinksetUseMaterialVolume->setEnabled(FALSE);
-			mEditLinksetUseExclusionVolume->setEnabled(FALSE);
-			mEditLinksetUseDynamicPhantom->setEnabled(FALSE);
-			break;
-		case kAllowLinksetUseOnlyPhantom :
-			mEditLinksetUseWalkable->setEnabled(FALSE);
-			mEditLinksetUseStaticObstacle->setEnabled(FALSE);
-			mEditLinksetUseDynamicObstacle->setEnabled(FALSE);
-			mEditLinksetUseMaterialVolume->setEnabled(TRUE);
-			mEditLinksetUseExclusionVolume->setEnabled(TRUE);
-			mEditLinksetUseDynamicPhantom->setEnabled(TRUE);
-			break;
-		case kAllowLinksetUseOnlyTerrain :
-			mEditLinksetUseWalkable->setEnabled(TRUE);
-			mEditLinksetUseStaticObstacle->setEnabled(FALSE);
-			mEditLinksetUseDynamicObstacle->setEnabled(FALSE);
-			mEditLinksetUseMaterialVolume->setEnabled(FALSE);
-			mEditLinksetUseExclusionVolume->setEnabled(FALSE);
-			mEditLinksetUseDynamicPhantom->setEnabled(FALSE);
-			break;
-		default : 
-			llassert(0);
-			break;
-		}
+		updateEnableStateOnEditLinksetUse();
 	}
 }
 
@@ -785,10 +746,18 @@ LLSD LLFloaterPathfindingLinksets::buildLinksetScrollListElement(const LLPathfin
 	{
 		linksetUse += (" " + getString("linkset_is_terrain"));
 	}
-	else if (!pLinksetPtr->isModifiable())
+	else if (!pLinksetPtr->isModifiable() && pLinksetPtr->canBeVolume())
 	{
 		linksetUse += (" " + getString("linkset_is_restricted_state"));
 	}
+	else if (pLinksetPtr->isModifiable() && !pLinksetPtr->canBeVolume())
+	{
+		linksetUse += (" " + getString("linkset_is_non_volume_state"));
+	}
+	else if (!pLinksetPtr->isModifiable() && !pLinksetPtr->canBeVolume())
+	{
+		linksetUse += (" " + getString("linkset_is_restricted_non_volume_state"));
+	}
 	columns[4]["value"] = linksetUse;
 	columns[4]["font"] = "SANSSERIF";
 
@@ -831,68 +800,32 @@ LLSD LLFloaterPathfindingLinksets::buildLinksetUseScrollListElement(const std::s
 	return element;
 }
 
-LLFloaterPathfindingLinksets::EAllowLinksetsUse LLFloaterPathfindingLinksets::getAllowLinksetUse() const
+bool LLFloaterPathfindingLinksets::isShowUnmodifiablePhantomWarning(LLPathfindingLinkset::ELinksetUse linksetUse) const
 {
-	EAllowLinksetsUse allowLinksetUse = kAllowLinksetUseAll;
+	bool showWarning = false;
 
-	std::vector<LLScrollListItem*> selectedItems = mLinksetsScrollList->getAllSelected();
-	if (!selectedItems.empty())
+	if (linksetUse != LLPathfindingLinkset::kUnknown)
 	{
-		bool isAllLocked = true;
-		bool isAllPhantom = true;
-		bool isAllNonPhantom = true;
-		bool isAllTerrain = true;
-
-		for (std::vector<LLScrollListItem*>::const_iterator selectedItemIter = selectedItems.begin();
-			(isAllLocked || isAllPhantom || isAllNonPhantom) && (selectedItemIter != selectedItems.end()); ++selectedItemIter)
-		{
-			const LLScrollListItem *selectedItem = *selectedItemIter;
-			llassert(mLinksetsListPtr != NULL);
-			LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->find(selectedItem->getUUID().asString());
-			llassert(linksetIter != mLinksetsListPtr->end());
-			const LLPathfindingLinksetPtr linksetPtr = linksetIter->second;
-			if (!linksetPtr->isTerrain())
-			{
-				isAllTerrain = false;
-			}
-			if (!linksetPtr->isModifiable())
-			{
-				if (linksetPtr->isPhantom())
-				{
-					isAllNonPhantom = false;
-				}
-				else
-				{
-					isAllPhantom = false;
-				}
-			}
-			else
-			{
-				isAllLocked = false;
-			}
-		}
-
-		if (isAllTerrain)
-		{
-			allowLinksetUse = kAllowLinksetUseOnlyTerrain;
-		}
-		else if (isAllLocked)
+		std::vector<LLScrollListItem*> selectedItems = mLinksetsScrollList->getAllSelected();
+		if (!selectedItems.empty())
 		{
-			if (isAllPhantom && !isAllNonPhantom)
-			{
-				allowLinksetUse = kAllowLinksetUseOnlyPhantom;
-			}
-			else if (isAllNonPhantom && !isAllPhantom)
+			for (std::vector<LLScrollListItem*>::const_iterator selectedItemIter = selectedItems.begin();
+				!showWarning && (selectedItemIter != selectedItems.end()); ++selectedItemIter)
 			{
-				allowLinksetUse = kAllowLinksetUseOnlyNonPhantom;
+				const LLScrollListItem *selectedItem = *selectedItemIter;
+				llassert(mLinksetsListPtr != NULL);
+				LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->find(selectedItem->getUUID().asString());
+				llassert(linksetIter != mLinksetsListPtr->end());
+				const LLPathfindingLinksetPtr linksetPtr = linksetIter->second;
+				showWarning = linksetPtr->isShowUnmodifiablePhantomWarning(linksetUse);
 			}
 		}
 	}
 
-	return allowLinksetUse;
+	return showWarning;
 }
 
-bool LLFloaterPathfindingLinksets::doShowLinksetUseSetWarning(LLPathfindingLinkset::ELinksetUse linksetUse) const
+bool LLFloaterPathfindingLinksets::isShowCannotBeVolumeWarning(LLPathfindingLinkset::ELinksetUse linksetUse) const
 {
 	bool showWarning = false;
 
@@ -909,7 +842,7 @@ bool LLFloaterPathfindingLinksets::doShowLinksetUseSetWarning(LLPathfindingLinks
 				LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->find(selectedItem->getUUID().asString());
 				llassert(linksetIter != mLinksetsListPtr->end());
 				const LLPathfindingLinksetPtr linksetPtr = linksetIter->second;
-				showWarning = (!linksetPtr->isModifiable() && (linksetPtr->isPhantom() != LLPathfindingLinkset::isPhantom(linksetUse)));
+				showWarning = linksetPtr->isShowCannotBeVolumeWarning(linksetUse);
 			}
 		}
 	}
@@ -1039,17 +972,101 @@ void LLFloaterPathfindingLinksets::updateEnableStateOnEditFields()
 	mApplyEditsButton->setEnabled(isEditEnabled && (getMessagingState() == kMessagingComplete));
 }
 
+void LLFloaterPathfindingLinksets::updateEnableStateOnEditLinksetUse()
+{
+	BOOL useWalkable = FALSE;
+	BOOL useStaticObstacle = FALSE;
+	BOOL useDynamicObstacle = FALSE;
+	BOOL useMaterialVolume = FALSE;
+	BOOL useExclusionVolume = FALSE;
+	BOOL useDynamicPhantom = FALSE;
+
+	std::vector<LLScrollListItem*> selectedItems = mLinksetsScrollList->getAllSelected();
+	if (!selectedItems.empty())
+	{
+		for (std::vector<LLScrollListItem*>::const_iterator selectedItemIter = selectedItems.begin();
+			!(useWalkable && useStaticObstacle && useDynamicObstacle && useMaterialVolume && useExclusionVolume && useDynamicPhantom) && (selectedItemIter != selectedItems.end());
+			++selectedItemIter)
+		{
+			const LLScrollListItem *selectedItem = *selectedItemIter;
+			llassert(mLinksetsListPtr != NULL);
+			LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->find(selectedItem->getUUID().asString());
+			llassert(linksetIter != mLinksetsListPtr->end());
+			const LLPathfindingLinksetPtr linksetPtr = linksetIter->second;
+
+			if (linksetPtr->isTerrain())
+			{
+				useWalkable = TRUE;
+			}
+			else
+			{
+				if (linksetPtr->isModifiable())
+				{
+					useWalkable = TRUE;
+					useStaticObstacle = TRUE;
+					useDynamicObstacle = TRUE;
+					useDynamicPhantom = TRUE;
+					if (linksetPtr->canBeVolume())
+					{
+						useMaterialVolume = TRUE;
+						useExclusionVolume = TRUE;
+					}
+				}
+				else if (linksetPtr->isPhantom())
+				{
+					useDynamicPhantom = TRUE;
+					if (linksetPtr->canBeVolume())
+					{
+						useMaterialVolume = TRUE;
+						useExclusionVolume = TRUE;
+					}
+				}
+				else
+				{
+					useWalkable = TRUE;
+					useStaticObstacle = TRUE;
+					useDynamicObstacle = TRUE;
+				}
+			}
+		}
+	}
+
+	mEditLinksetUseWalkable->setEnabled(useWalkable);
+	mEditLinksetUseStaticObstacle->setEnabled(useStaticObstacle);
+	mEditLinksetUseDynamicObstacle->setEnabled(useDynamicObstacle);
+	mEditLinksetUseMaterialVolume->setEnabled(useMaterialVolume);
+	mEditLinksetUseExclusionVolume->setEnabled(useExclusionVolume);
+	mEditLinksetUseDynamicPhantom->setEnabled(useDynamicPhantom);
+}
+
 void LLFloaterPathfindingLinksets::applyEdit()
 {
 	LLPathfindingLinkset::ELinksetUse linksetUse = getEditLinksetUse();
 
-	if (doShowLinksetUseSetWarning(linksetUse))
+	bool showUnmodifiablePhantomWarning = isShowUnmodifiablePhantomWarning(linksetUse);
+	bool showCannotBeVolumeWarning = isShowCannotBeVolumeWarning(linksetUse);
+
+	if (showUnmodifiablePhantomWarning || showCannotBeVolumeWarning)
 	{
 		LLPathfindingLinkset::ELinksetUse restrictedLinksetUse = LLPathfindingLinkset::getLinksetUseWithToggledPhantom(linksetUse);
 		LLSD substitutions;
 		substitutions["REQUESTED_TYPE"] = getLinksetUseString(linksetUse);
 		substitutions["RESTRICTED_TYPE"] = getLinksetUseString(restrictedLinksetUse);
-		LLNotificationsUtil::add("PathfindingLinksets_SetLinksetUseMismatchOnRestricted", substitutions, LLSD(), boost::bind(&LLFloaterPathfindingLinksets::handleApplyEdit, this, _1, _2));
+
+		std::string notificationName;
+		if (showUnmodifiablePhantomWarning && showCannotBeVolumeWarning)
+		{
+			notificationName = "PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume";
+		}
+		else if (showUnmodifiablePhantomWarning)
+		{
+			notificationName = "PathfindingLinksets_SetLinksetUseMismatchOnRestricted";
+		}
+		else
+		{
+			notificationName = "PathfindingLinksets_SetLinksetUseMismatchOnVolume";
+		}
+		LLNotificationsUtil::add(notificationName, substitutions, LLSD(), boost::bind(&LLFloaterPathfindingLinksets::handleApplyEdit, this, _1, _2));
 	}
 	else
 	{
diff --git a/indra/newview/llfloaterpathfindinglinksets.h b/indra/newview/llfloaterpathfindinglinksets.h
index d201ffe20bf..fdc47e3d400 100644
--- a/indra/newview/llfloaterpathfindinglinksets.h
+++ b/indra/newview/llfloaterpathfindinglinksets.h
@@ -35,8 +35,8 @@
 #include "llpathfindinglinksetlist.h"
 #include "llpathfindingmanager.h"
 
-#include <boost/signals2.hpp>
-
+#include <boost/signals2.hpp>
+
 class LLSD;
 class LLUICtrl;
 class LLTextBase;
@@ -77,14 +77,6 @@ class LLFloaterPathfindingLinksets
 protected:
 
 private:
-	typedef enum
-	{
-		kAllowLinksetUseAll,
-		kAllowLinksetUseOnlyNonPhantom,
-		kAllowLinksetUseOnlyPhantom,
-		kAllowLinksetUseOnlyTerrain
-	} EAllowLinksetsUse;
-
 	LLLineEditor     *mFilterByName;
 	LLLineEditor     *mFilterByDescription;
 	LLComboBox       *mFilterByLinksetUse;
@@ -122,7 +114,7 @@ class LLFloaterPathfindingLinksets
 	LLPathfindingLinksetListPtr              mLinksetsListPtr;
 	LLObjectSelectionHandle                  mLinksetsSelection;
 	LLPathfindingManager::agent_state_slot_t mAgentStateSlot;
-	boost::signals2::connection              mSelectionUpdateSlot;
+	boost::signals2::connection              mSelectionUpdateSlot;
 
 	// Does its own instance management, so clients not allowed
 	// to allocate or destroy.
@@ -163,12 +155,13 @@ class LLFloaterPathfindingLinksets
 	LLSD buildLinksetScrollListElement(const LLPathfindingLinksetPtr pLinksetPtr, const LLVector3 &pAvatarPosition) const;
 	LLSD buildLinksetUseScrollListElement(const std::string &label, S32 value) const;
 
-	EAllowLinksetsUse getAllowLinksetUse() const;
-	bool              doShowLinksetUseSetWarning(LLPathfindingLinkset::ELinksetUse linksetUse) const;
+	bool isShowUnmodifiablePhantomWarning(LLPathfindingLinkset::ELinksetUse linksetUse) const;
+	bool isShowCannotBeVolumeWarning(LLPathfindingLinkset::ELinksetUse linksetUse) const;
 
 	void updateStatusMessage();
 	void updateEnableStateOnListActions();
 	void updateEnableStateOnEditFields();
+	void updateEnableStateOnEditLinksetUse();
 
 	void applyEdit();
 	void handleApplyEdit(const LLSD &pNotification, const LLSD &pResponse);
diff --git a/indra/newview/llpathfindinglinkset.cpp b/indra/newview/llpathfindinglinkset.cpp
index 1300022e8bc..f2d95d9221c 100644
--- a/indra/newview/llpathfindinglinkset.cpp
+++ b/indra/newview/llpathfindinglinkset.cpp
@@ -31,15 +31,16 @@
 #include "v3math.h"
 #include "lluuid.h"
 
-#define LINKSET_NAME_FIELD                  "name"
-#define LINKSET_DESCRIPTION_FIELD           "description"
-#define LINKSET_LAND_IMPACT_FIELD           "landimpact"
-#define LINKSET_MODIFIABLE_FIELD            "modifiable"
+#define LINKSET_NAME_FIELD                 "name"
+#define LINKSET_DESCRIPTION_FIELD          "description"
+#define LINKSET_LAND_IMPACT_FIELD          "landimpact"
+#define LINKSET_MODIFIABLE_FIELD           "modifiable"
 #ifdef DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
-#define DEPRECATED_LINKSET_PERMANENT_FIELD  "permanent"
+#define DEPRECATED_LINKSET_PERMANENT_FIELD "permanent"
 #define DEPRECATED_LINKSET_WALKABLE_FIELD  "walkable"
 #endif // DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
 #define LINKSET_CATEGORY_FIELD             "navmesh_category"
+#define LINKSET_CAN_BE_VOLUME              "can_be_volume"
 #define LINKSET_PHANTOM_FIELD              "phantom"
 #define LINKSET_WALKABILITY_A_FIELD        "A"
 #define LINKSET_WALKABILITY_B_FIELD        "B"
@@ -68,7 +69,8 @@ LLPathfindingLinkset::LLPathfindingLinkset(const LLSD& pTerrainLinksetItem)
 #ifdef MISSING_MODIFIABLE_FIELD_WAR
 	mHasModifiable(true),
 #endif // MISSING_MODIFIABLE_FIELD_WAR
-	mIsModifiable(TRUE),
+	mIsModifiable(FALSE),
+	mCanBeVolume(FALSE),
 	mLinksetUse(kUnknown),
 	mWalkabilityCoefficientA(MIN_WALKABILITY_VALUE),
 	mWalkabilityCoefficientB(MIN_WALKABILITY_VALUE),
@@ -89,6 +91,7 @@ LLPathfindingLinkset::LLPathfindingLinkset(const std::string &pUUID, const LLSD&
 	mHasModifiable(false),
 #endif // MISSING_MODIFIABLE_FIELD_WAR
 	mIsModifiable(TRUE),
+	mCanBeVolume(TRUE),
 	mLinksetUse(kUnknown),
 	mWalkabilityCoefficientA(MIN_WALKABILITY_VALUE),
 	mWalkabilityCoefficientB(MIN_WALKABILITY_VALUE),
@@ -111,6 +114,7 @@ LLPathfindingLinkset::LLPathfindingLinkset(const LLPathfindingLinkset& pOther)
 #else // MISSING_MODIFIABLE_FIELD_WAR
 	mIsModifiable(pOther.mIsModifiable),
 #endif // MISSING_MODIFIABLE_FIELD_WAR
+	mCanBeVolume(pOther.mCanBeVolume),
 	mLinksetUse(pOther.mLinksetUse),
 	mWalkabilityCoefficientA(pOther.mWalkabilityCoefficientA),
 	mWalkabilityCoefficientB(pOther.mWalkabilityCoefficientB),
@@ -139,6 +143,7 @@ LLPathfindingLinkset& LLPathfindingLinkset::operator =(const LLPathfindingLinkse
 #else // MISSING_MODIFIABLE_FIELD_WAR
 	mIsModifiable = pOther.mIsModifiable;
 #endif // MISSING_MODIFIABLE_FIELD_WAR
+	mCanBeVolume = pOther.mCanBeVolume;
 	mLinksetUse = pOther.mLinksetUse;
 	mWalkabilityCoefficientA = pOther.mWalkabilityCoefficientA;
 	mWalkabilityCoefficientB = pOther.mWalkabilityCoefficientB;
@@ -153,32 +158,6 @@ BOOL LLPathfindingLinkset::isPhantom() const
 	return isPhantom(getLinksetUse());
 }
 
-BOOL LLPathfindingLinkset::isPhantom(ELinksetUse pLinksetUse)
-{
-	BOOL retVal;
-
-	switch (pLinksetUse)
-	{
-	case kWalkable :
-	case kStaticObstacle :
-	case kDynamicObstacle :
-		retVal = false;
-		break;
-	case kMaterialVolume :
-	case kExclusionVolume :
-	case kDynamicPhantom :
-		retVal = true;
-		break;
-	case kUnknown :
-	default :
-		retVal = false;
-		llassert(0);
-		break;
-	}
-
-	return retVal;
-}
-
 LLPathfindingLinkset::ELinksetUse LLPathfindingLinkset::getLinksetUseWithToggledPhantom(ELinksetUse pLinksetUse)
 {
 	BOOL isPhantom = LLPathfindingLinkset::isPhantom(pLinksetUse);
@@ -187,19 +166,31 @@ LLPathfindingLinkset::ELinksetUse LLPathfindingLinkset::getLinksetUseWithToggled
 	return getLinksetUse(!isPhantom, navMeshGenerationCategory);
 }
 
+bool LLPathfindingLinkset::isShowUnmodifiablePhantomWarning(ELinksetUse pLinksetUse) const
+{
+	return (!isModifiable() && (isPhantom() != isPhantom(pLinksetUse)));
+}
+
+bool LLPathfindingLinkset::isShowCannotBeVolumeWarning(ELinksetUse pLinksetUse) const
+{
+	return (!canBeVolume() && ((pLinksetUse == kMaterialVolume) || (pLinksetUse == kExclusionVolume)));
+}
+
 LLSD LLPathfindingLinkset::encodeAlteredFields(ELinksetUse pLinksetUse, S32 pA, S32 pB, S32 pC, S32 pD) const
 {
 	LLSD itemData;
 
-	if (!isTerrain() && (pLinksetUse != kUnknown) && (mLinksetUse != pLinksetUse))
+	if (!isTerrain() && (pLinksetUse != kUnknown) && (getLinksetUse() != pLinksetUse) &&
+		(canBeVolume() || ((pLinksetUse != kMaterialVolume) && (pLinksetUse != kExclusionVolume))))
 	{
-		if (mIsModifiable)
+		if (isModifiable())
 		{
-			itemData[LINKSET_PHANTOM_FIELD] = static_cast<bool>(LLPathfindingLinkset::isPhantom(pLinksetUse));
+			itemData[LINKSET_PHANTOM_FIELD] = static_cast<bool>(isPhantom(pLinksetUse));
 		}
+
 #ifdef DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
-		itemData[DEPRECATED_LINKSET_PERMANENT_FIELD] = static_cast<bool>(LLPathfindingLinkset::isPermanent(pLinksetUse));
-		itemData[DEPRECATED_LINKSET_WALKABLE_FIELD] = static_cast<bool>(LLPathfindingLinkset::isWalkable(pLinksetUse));
+		itemData[DEPRECATED_LINKSET_PERMANENT_FIELD] = static_cast<bool>(isPermanent(pLinksetUse));
+		itemData[DEPRECATED_LINKSET_WALKABLE_FIELD] = static_cast<bool>(isWalkable(pLinksetUse));
 #endif // DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
 		itemData[LINKSET_CATEGORY_FIELD] = convertCategoryToLLSD(getNavMeshGenerationCategory(pLinksetUse));
 	}
@@ -290,7 +281,13 @@ void LLPathfindingLinkset::parsePathfindingData(const LLSD &pLinksetItem)
 	llassert(pLinksetItem.has(LINKSET_CATEGORY_FIELD));
 	mLinksetUse = getLinksetUse(isPhantom, convertCategoryFromLLSD(pLinksetItem.get(LINKSET_CATEGORY_FIELD)));
 #endif // DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
-	
+
+	if (pLinksetItem.has(LINKSET_CAN_BE_VOLUME))
+	{
+		llassert(pLinksetItem.get(LINKSET_CAN_BE_VOLUME).isBoolean());
+		mCanBeVolume = pLinksetItem.get(LINKSET_CAN_BE_VOLUME).asBoolean();
+	}
+
 	llassert(pLinksetItem.has(LINKSET_WALKABILITY_A_FIELD));
 	llassert(pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).isInteger());
 	mWalkabilityCoefficientA = pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).asInteger();
@@ -376,6 +373,32 @@ BOOL LLPathfindingLinkset::isWalkable(ELinksetUse pLinksetUse)
 }
 #endif // DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
 
+BOOL LLPathfindingLinkset::isPhantom(ELinksetUse pLinksetUse)
+{
+	BOOL retVal;
+
+	switch (pLinksetUse)
+	{
+	case kWalkable :
+	case kStaticObstacle :
+	case kDynamicObstacle :
+		retVal = false;
+		break;
+	case kMaterialVolume :
+	case kExclusionVolume :
+	case kDynamicPhantom :
+		retVal = true;
+		break;
+	case kUnknown :
+	default :
+		retVal = false;
+		llassert(0);
+		break;
+	}
+
+	return retVal;
+}
+
 LLPathfindingLinkset::ELinksetUse LLPathfindingLinkset::getLinksetUse(bool pIsPhantom, ENavMeshGenerationCategory pNavMeshGenerationCategory)
 {
 	ELinksetUse linksetUse = kUnknown;
diff --git a/indra/newview/llpathfindinglinkset.h b/indra/newview/llpathfindinglinkset.h
index 49250dc7486..d8c1c9d7955 100644
--- a/indra/newview/llpathfindinglinkset.h
+++ b/indra/newview/llpathfindinglinkset.h
@@ -44,13 +44,6 @@ typedef boost::shared_ptr<LLPathfindingLinkset> LLPathfindingLinksetPtr;
 class LLPathfindingLinkset
 {
 public:
-	typedef enum
-	{
-		kNavMeshGenerationIgnore,
-		kNavMeshGenerationInclude,
-		kNavMeshGenerationExclude
-	} ENavMeshGenerationCategory;
-
 	typedef enum
 	{
 		kUnknown,
@@ -77,7 +70,7 @@ class LLPathfindingLinkset
 	inline const LLVector3&           getLocation() const                 {return mLocation;};
 	BOOL                              isModifiable() const                {return mIsModifiable;};
 	BOOL                              isPhantom() const;
-	static BOOL                       isPhantom(ELinksetUse pLinksetUse);
+	BOOL                              canBeVolume() const                 {return mCanBeVolume;};
 	static ELinksetUse                getLinksetUseWithToggledPhantom(ELinksetUse pLinksetUse);
 
 	inline ELinksetUse                getLinksetUse() const               {return mLinksetUse;};
@@ -87,6 +80,8 @@ class LLPathfindingLinkset
 	inline S32                        getWalkabilityCoefficientC() const  {return mWalkabilityCoefficientC;};
 	inline S32                        getWalkabilityCoefficientD() const  {return mWalkabilityCoefficientD;};
 
+	bool                              isShowUnmodifiablePhantomWarning(ELinksetUse pLinksetUse) const;
+	bool                              isShowCannotBeVolumeWarning(ELinksetUse pLinksetUse) const;
 	LLSD                              encodeAlteredFields(ELinksetUse pLinksetUse, S32 pA, S32 pB, S32 pC, S32 pD) const;
 
 	static const S32 MIN_WALKABILITY_VALUE;
@@ -95,6 +90,13 @@ class LLPathfindingLinkset
 protected:
 
 private:
+	typedef enum
+	{
+		kNavMeshGenerationIgnore,
+		kNavMeshGenerationInclude,
+		kNavMeshGenerationExclude
+	} ENavMeshGenerationCategory;
+
 	void                              parseObjectData(const LLSD &pLinksetItem);
 	void                              parsePathfindingData(const LLSD &pLinksetItem);
 
@@ -103,6 +105,7 @@ class LLPathfindingLinkset
 	static BOOL                       isPermanent(ELinksetUse pLinksetUse);
 	static BOOL                       isWalkable(ELinksetUse pLinksetUse);
 #endif // DEPRECATED_NAVMESH_PERMANENT_WALKABLE_FLAGS
+	static BOOL                       isPhantom(ELinksetUse pLinksetUse);
 	static ELinksetUse                getLinksetUse(bool pIsPhantom, ENavMeshGenerationCategory pNavMeshGenerationCategory);
 	static ENavMeshGenerationCategory getNavMeshGenerationCategory(ELinksetUse pLinksetUse);
 	static LLSD                       convertCategoryToLLSD(ENavMeshGenerationCategory pNavMeshGenerationCategory);
@@ -118,6 +121,7 @@ class LLPathfindingLinkset
 	bool         mHasModifiable;
 #endif // MISSING_MODIFIABLE_FIELD_WAR
 	BOOL         mIsModifiable;
+	BOOL         mCanBeVolume;
 	ELinksetUse  mLinksetUse;
 	S32          mWalkabilityCoefficientA;
 	S32          mWalkabilityCoefficientB;
diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
index d00b85f5d73..7cd6c32f7e4 100644
--- a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
+++ b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
@@ -34,6 +34,8 @@
   <floater.string name="linkset_use_dynamic_phantom">Movable phantom</floater.string>
   <floater.string name="linkset_is_terrain">[unmodifiable]</floater.string>
   <floater.string name="linkset_is_restricted_state">[restricted]</floater.string>
+  <floater.string name="linkset_is_non_volume_state">[concave]</floater.string>
+  <floater.string name="linkset_is_restricted_non_volume_state">[restricted,concave]</floater.string>
   <floater.string name="linkset_choose_use">Choose linkset use...</floater.string>
   <panel
       border="false"
@@ -178,7 +180,7 @@
       <scroll_list.columns
           label="Description (root prim)"
           name="description"
-          width="192" />
+          width="166" />
       <scroll_list.columns
           label="Land impact"
           name="land_impact"
@@ -190,7 +192,7 @@
       <scroll_list.columns
           label="Linkset use"
           name="linkset_use"
-          width="210" />
+          width="236" />
       <scroll_list.columns
           label="A %"
           name="a_percent"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 5faafecef7a..5d6cefbcfb3 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -7677,6 +7677,33 @@ The site at &apos;&lt;nolink&gt;[HOST_NAME]&lt;/nolink&gt;&apos; in realm &apos;
      yestext="OK"/>
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"
+   type="alertmodal">
+    Some selected linksets cannot be set to be '[REQUESTED_TYPE]' because of the shape is non-convex.
+    <tag>confirm</tag>
+    <usetemplate
+     ignoretext="Do you wish to proceed?"
+     name="okcancelignore"
+     notext="Cancel"
+     yestext="OK"/>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"
+   type="alertmodal">
+    Some selected linksets cannot be set to be '[REQUESTED_TYPE]' because of permission restrictions on the linkset.  These linksets will be set to be '[RESTRICTED_TYPE]' instead.
+ Some selected linksets cannot be set to be '[REQUESTED_TYPE]' because of the shape is non-convex. These linksets use types will not change.
+    <tag>confirm</tag>
+    <usetemplate
+     ignoretext="Do you wish to proceed?"
+     name="okcancelignore"
+     notext="Cancel"
+     yestext="OK"/>
+  </notification>
+
   <global name="UnsupportedGLRequirements">
 You do not appear to have the proper hardware requirements for [APP_NAME]. [APP_NAME] requires an OpenGL graphics card that has multitexture support. If this is the case, you may want to make sure that you have the latest drivers for your graphics card, and service packs and patches for your operating system.
 
-- 
GitLab