diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8feed4d16a17a53a241e1e7967e453789c16d6c6..6dd63b95c206c31f69af4b05cb18cc1687ca2970 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -16628,6 +16628,17 @@
       <key>Value</key>
       <integer>1</integer>        
     </map>
+    <key>CurrentlyUsingBakesOnMesh</key>
+    <map>
+      <key>Comment</key>
+      <string>Are we currently on a grid that uses bakes on mesh? Persisted to force rebakes on login to named grids.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
 </map>
 </llsd>
 
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index d6ba90114bdd1893627f019de13b740d0d9e2e86..2691134aa1b2453d394e46073d364bca9cb132c1 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -4813,7 +4813,7 @@ void LLAgent::sendAgentSetAppearance()
 	// KLW - TAT this will probably need to check the local queue.
 	BOOL textures_current = gAgentAvatarp->areTexturesCurrent();
 
-	for(U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++ )
+	for(U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++ )
 	{
 		const ETextureIndex texture_index = LLAvatarAppearanceDictionary::bakedToLocalTextureIndex((EBakedTextureIndex)baked_index);
 
@@ -4846,7 +4846,7 @@ void LLAgent::sendAgentSetAppearance()
 			dumpSentAppearance(dump_prefix);
 		}
 		LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL;
-		for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
+		for (U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++)
 		{
 			BOOL generate_valid_hash = TRUE;
 			if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLAvatarAppearanceDefines::EBakedTextureIndex)baked_index))
@@ -4855,6 +4855,12 @@ void LLAgent::sendAgentSetAppearance()
 				LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << LL_ENDL;
 			}
 
+			if (baked_index == BAKED_SKIRT && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
+			{
+				LL_DEBUGS("Avatar") << "Not caching baked texture for unworn skirt." << LL_ENDL;
+				generate_valid_hash = FALSE;
+			}
+
 			const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash);
 			if (hash.notNull())
 			{
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index e2a5a2224ddc1a4e0ff3c43c96e202986e138639..48139cb2c133222429497b1f91241e45e415d678 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -1355,6 +1355,16 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 		LLPointer<LLInventoryItem> new_item = items[i];
 
 		const LLWearableType::EType type = new_wearable->getType();
+		if(!gAgent.getRegion()->bakesOnMeshEnabled())
+		{
+			if(type == LLWearableType::WT_UNIVERSAL)
+			{
+				LL_DEBUGS("Avatar") << "Universal wearable not supported on this region - ignoring." << LL_ENDL;
+				mismatched++;
+				continue;
+			}
+		}
+
 		if (type < 0 || type>=LLWearableType::WT_COUNT)
 		{
 			LL_WARNS() << "invalid type " << type << LL_ENDL;
@@ -1659,7 +1669,7 @@ void LLAgentWearables::queryWearableCache()
 	gMessageSystem->addS32Fast(_PREHASH_SerialNum, gAgentQueryManager.mWearablesCacheQueryID);
 
 	S32 num_queries = 0;
-	for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
+	for (U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++)
 	{
 		LLUUID hash_id = computeBakedTextureHash((EBakedTextureIndex) baked_index);
 		if (hash_id.notNull())
diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
index ba614a2aa8169328084b2bac5b53856e52ca595b..6e63b1449b6915031e5aa21c419756fc58bc9561 100644
--- a/indra/newview/llviewerparceloverlay.cpp
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -84,7 +84,7 @@ LLViewerParcelOverlay::LLViewerParcelOverlay(LLViewerRegion* region, F32 region_
 	{
 		raw[i] = 0;
 	}
-	//mTexture->setSubImage(mImageRaw, 0, 0, mParcelGridsPerEdge, mParcelGridsPerEdge);
+	mTexture->setSubImage(mImageRaw, 0, 0, mParcelGridsPerEdge, mParcelGridsPerEdge);
 
 	// Create storage for ownership information from simulator
 	// and initialize it.
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 590ca1c09aca360e867dc04c04faba9c832f8d0c..7ddbf14e3a81a2879f2bee13abab162d9643c995 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -560,7 +560,13 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
 	mDead(FALSE),
 	mPaused(FALSE),
 	mRegionCacheHitCount(0),
-	mRegionCacheMissCount(0)
+	mRegionCacheMissCount(0),
+	mMaxBakes(LLGridManager::getInstance()->isInSecondlife()?
+		LLAvatarAppearanceDefines::EBakedTextureIndex::BAKED_NUM_INDICES:
+		LLAvatarAppearanceDefines::EBakedTextureIndex::BAKED_LEFT_ARM),
+	mMaxTEs(LLGridManager::getInstance()->isInSecondlife()?
+		LLAvatarAppearanceDefines::ETextureIndex::TEX_NUM_INDICES:
+		LLAvatarAppearanceDefines::ETextureIndex::TEX_HEAD_UNIVERSAL_TATTOO)
 {
 	mImpl->mOriginGlobal = from_region_handle(handle); 
 	updateRenderMatrix();
@@ -2311,6 +2317,17 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
             LLCurrencyWrapper::instance().setCurrency(cur_symbol);
         }
 	}
+	
+	if (mSimulatorFeatures.has("BakesOnMeshEnabled") && (mSimulatorFeatures["BakesOnMeshEnabled"].asBoolean()==true))
+	{
+		mMaxBakes = LLAvatarAppearanceDefines::EBakedTextureIndex::BAKED_NUM_INDICES;
+		mMaxTEs   = LLAvatarAppearanceDefines::ETextureIndex::TEX_NUM_INDICES;
+	}
+	else
+	{
+		mMaxBakes = LLAvatarAppearanceDefines::EBakedTextureIndex::BAKED_LEFT_ARM;
+		mMaxTEs   = LLAvatarAppearanceDefines::ETextureIndex::TEX_HEAD_UNIVERSAL_TATTOO;
+	}
 	setSimulatorFeaturesReceived(true);
 }
 
diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp
index 54308bd19e907fe033b8e8ee6acab43e8193f04d..a2cde9e970d0b8ddd0aa2c641f0b939f2d1c1092 100644
--- a/indra/newview/llviewertexlayer.cpp
+++ b/indra/newview/llviewertexlayer.cpp
@@ -243,8 +243,16 @@ void LLViewerTexLayerSetBuffer::midRenderTexLayerSet(BOOL success)
 			LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
 			if (layer_set->isVisible())
 			{
-				layer_set->getAvatar()->debugBakedTextureUpload(layer_set->getBakedTexIndex(), FALSE); // FALSE for start of upload, TRUE for finish.
-				doUpload();
+				auto bakedTexIdx = layer_set->getBakedTexIndex();
+				if(bakedTexIdx <= layer_set->getAvatar()->getNumBakes())
+				{
+					layer_set->getAvatar()->debugBakedTextureUpload(bakedTexIdx, FALSE); // FALSE for start of upload, TRUE for finish.
+					doUpload();
+				}
+				else
+				{
+					LL_DEBUGS("Avatar") << "Skipping bake for unsupported layer on this region" << LL_ENDL;
+				}
 			}
 			else
 			{
diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp
index b21f9016a6977125259547cbe1885dfb837c8f1f..3ac235811ce40d64b68552be136db7a173b20f43 100644
--- a/indra/newview/llviewerwearable.cpp
+++ b/indra/newview/llviewerwearable.cpp
@@ -49,7 +49,7 @@ class LLOverrideBakedTextureUpdate
 public:
 	LLOverrideBakedTextureUpdate(bool temp_state)
 	{
-		U32 num_bakes = (U32) LLAvatarAppearanceDefines::BAKED_NUM_INDICES;
+		U32 num_bakes = (U32) gAgentAvatarp->getNumBakes();
 		for( U32 index = 0; index < num_bakes; ++index )
 		{
 			composite_enabled[index] = gAgentAvatarp->isCompositeUpdateEnabled(index);
@@ -59,7 +59,7 @@ public:
 
 	~LLOverrideBakedTextureUpdate()
 	{
-		U32 num_bakes = (U32)LLAvatarAppearanceDefines::BAKED_NUM_INDICES;		
+		U32 num_bakes = (U32) gAgentAvatarp->getNumBakes();
 		for( U32 index = 0; index < num_bakes; ++index )
 		{
 			gAgentAvatarp->setCompositeUpdatesEnabled(index, composite_enabled[index]);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 9fee6002ff019ba2cb5d172d0658331e14980d68..e933fa269af6d4b7afce2cc3c0a41211d429c84c 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -94,6 +94,7 @@
 #include "llviewertexlayer.h"
 #include "llviewertexturelist.h"
 #include "llviewermenu.h"
+#include "llviewernetwork.h"
 #include "llviewerobjectlist.h"
 #include "llviewerparcelmgr.h"
 #include "llviewerregion.h"
@@ -778,6 +779,26 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mVisuallyMuteSetting = LLVOAvatar::VisualMuteSettings(LLRenderMuteList::getInstance()->getSavedVisualMuteSetting(getID()));
 }
 
+S32 LLVOAvatar::getNumBakes() const 
+{
+	// BAKED_LEFT_ARM is equal to the pre-BOM BAKED_NUM_INDICES
+	if(getRegion())
+	{
+		// LL_INFOS("BOMOS") 
+		// 				<< getFullname()
+		// 				<< "Using avatar region settings [" << getRegion()->getName() << "]"
+		// 				<< " bakesOnMesh = " << static_cast<const char *>(getRegion()->bakesOnMeshEnabled()?"True":"False")
+		// 				<< LL_ENDL;
+		return getRegion()->getRegionMaxBakes();
+	}
+	// LL_INFOS("BOMOS") 
+	// 				<< " Using fallback settings"
+	// 				<< " bakesOnMesh = " << static_cast<const char *>(LLGridManager::instance().isInSecondLife()?"True":"False")
+	// 				<< LL_ENDL;
+	// fallback, in SL assume BOM, elsewhere assume not.
+	return LLGridManager::instance().isInSecondlife() ? BAKED_NUM_INDICES : BAKED_LEFT_ARM;
+}
+
 std::string LLVOAvatar::avString() const
 {
     if (isControlAvatar())
@@ -864,7 +885,7 @@ BOOL LLVOAvatar::isFullyBaked()
 	if (mIsDummy) return TRUE;
 	if (getNumTEs() == 0) return FALSE;
 
-	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+	for (U32 i = 0; i < getNumBakes(); i++)
 	{
 		if (!isTextureDefined(mBakedTextureDatas[i].mTextureIndex)
 			&& ((i != BAKED_SKIRT) || isWearingWearableType(LLWearableType::WT_SKIRT))
@@ -1345,7 +1366,7 @@ void LLVOAvatar::calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {
     LL_RECORD_BLOCK_TIME(FTM_AVATAR_EXTENT_UPDATE);
 
-    static LLCachedControl<S32> box_detail(gSavedSettings, "AvatarBoundingBoxComplexity");
+	static const LLCachedControl<S32> box_detail(gSavedSettings, "AvatarBoundingBoxComplexity");
 
     // FIXME the update_min_max function used below assumes there is a
     // known starting point, but in general there isn't. Ideally the
@@ -3194,7 +3215,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 			debugAvatarRezTime("AvatarRezLeftAppearanceNotification","left appearance mode");
 		}
 	}
-	static LLCachedControl<bool> use_color_mgr(gSavedSettings, "AlchemyNametagColorMgr", false);
+	static LLCachedControl<bool> use_color_mgr(gSavedSettings, "AlchemyNametagColorMgr", true);
 	const LLColor4& name_tag_color = use_color_mgr ? isSelf() ? LLColor4::white : ALAvatarColorMgr::instance().getColor(getID()) : getNameTagColor(is_friend);
 
 	// Rebuild name tag if state change detected
@@ -8250,7 +8271,7 @@ void LLVOAvatar::updateMeshTextures()
 
 	mBakedTextureDebugText += fmt::format(FMT_STRING("{:06d}\n"),update_counter++);
 	mBakedTextureDebugText += "indx layerset linvld ltda ilb ulkg ltid\n";
-	for (U32 i=0; i < mBakedTextureDatas.size(); i++)
+	for (U32 i=0; i < getNumBakes(); i++)
 	{
 		is_layer_baked[i] = isTextureDefined(mBakedTextureDatas[i].mTextureIndex);
 		LLViewerTexLayerSet* layerset = nullptr;
@@ -8299,7 +8320,7 @@ void LLVOAvatar::updateMeshTextures()
 										   last_id_string.c_str());
 	}
 
-	for (U32 i=0; i < mBakedTextureDatas.size(); i++)
+	for (U32 i=0; i < getNumBakes(); i++)
 	{
 		debugColorizeSubMeshes(i, LLColor4::white);
 
@@ -8565,7 +8586,7 @@ void LLVOAvatar::releaseComponentTextures()
 		}
 	}
 
-	for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
+	for (U8 baked_index = 0; baked_index < getNumBakes(); baked_index++)
 	{
 		const LLAvatarAppearanceDictionary::BakedEntry * bakedDicEntry = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index);
 		// skip if this is a skirt and av is not wearing one, or if we don't have a baked texture UUID
@@ -9225,7 +9246,7 @@ void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& conte
 
 LLViewerTexture* LLVOAvatar::getBakedTexture(const U8 te)
 {
-	if (te < 0 || te >= BAKED_NUM_INDICES)
+	if (te < 0 || te >= getNumBakes())
 	{
 		return NULL;
 	}
@@ -10522,7 +10543,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
 		hud_complexity_list_t hud_complexity_list;
 
 		const auto& avatar_dictionary = LLAvatarAppearanceDictionary::instance();
-		for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
+		for (U8 baked_index = 0; baked_index < getNumBakes(); baked_index++)
 		{
 		    const LLAvatarAppearanceDictionary::BakedEntry *baked_dict
 				= avatar_dictionary.getBakedTexture((EBakedTextureIndex)baked_index);
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index f72cee4d3954b2a1f87f05ee7dc378fa66226afc..17de80b6f67fd3bf47009fdc49abc73907b89262 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -350,6 +350,7 @@ public:
 	BOOL			hasGray() const; 
 	S32				getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = textured, 3 = textured and fully downloaded.
 	void			updateRezzedStatusTimers();
+	S32 			getNumBakes() const;
 
 	S32				mLastRezzedStatus;
 
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index ea5fe3b9aeebdc63f9fb7870aa58f9bd9c4a9a43..700bb8aeb4c7e2668d87bb275551421a43eb1268 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -912,10 +912,31 @@ void LLVOAvatarSelf::removeMissingBakedTextures()
 	}
 }
 
+void LLVOAvatarSelf::checkBOMRebakeRequired()
+{
+	if(!getRegion())
+	{
+		auto newBOMStatus = getRegion()->bakesOnMeshEnabled();
+		static const LLCachedControl<bool> using_bom(gSavedSettings, "CurrentlyUsingBakesOnMesh", true);
+		if(!using_bom != newBOMStatus)
+		{
+			// force a rebake when the last grid we were on (including previous login) had different BOM support
+			// This replicates forceAppearanceUpdate rather than pulling in the whole of llavatarself.
+			if(!LLGridManager::instance().isInSecondlife())
+			{
+				doAfterInterval(boost::bind(&LLVOAvatarSelf::forceBakeAllTextures,	gAgentAvatarp.get(), true), 5.0);
+			}
+			// update the setting even if we are in SL so that switch SL to OS and back 
+			gSavedSettings.setBOOL("CurrentlyUsingBakesOnMesh", newBOMStatus);
+		}
+	}
+}
+
 void LLVOAvatarSelf::onSimulatorFeaturesReceived(const LLUUID& region_id)
 {
 	LL_INFOS("Avatar") << "simulator features received, setting hover based on region props" << LL_ENDL;
 	setHoverIfRegionEnabled();
+	checkBOMRebakeRequired(); // BOM we may have stale cache, rebake may be needed
 }
 
 //virtual
@@ -942,6 +963,7 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp)
 		if (regionp->simulatorFeaturesReceived())
 		{
 			setHoverIfRegionEnabled();
+			checkBOMRebakeRequired();// <FS:Beq/> BOM we may have stale cache, rebake may be needed
 		}
 		else
 		{
@@ -1767,7 +1789,7 @@ bool LLVOAvatarSelf::areTexturesCurrent() const
 // virtual
 bool LLVOAvatarSelf::hasPendingBakedUploads() const
 {
-	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+	for (U32 i = 0; i < getNumBakes(); i++)
 	{
 		LLViewerTexLayerSet* layerset = getTexLayerSet(i);
 		if (layerset && layerset->getViewerComposite() && layerset->getViewerComposite()->uploadPending())
@@ -2873,7 +2895,7 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 		}
 	}
 	LL_DEBUGS("Avatar") << "\t Time points for each upload (start / finish)" << LL_ENDL;
-	for (U32 i = 0; i < LLAvatarAppearanceDefines::BAKED_NUM_INDICES; ++i)
+	for (U32 i = 0; i < getNumBakes(); ++i)
 	{
 		LL_DEBUGS("Avatar") << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << LL_ENDL;
 	}
@@ -3056,7 +3078,7 @@ LLViewerTexLayerSet* LLVOAvatarSelf::getLayerSet(EBakedTextureIndex baked_index)
                case TEX_HEAD_BAKED:
                case TEX_HEAD_BODYPAINT:
                        return mHeadLayerSet; */
-       if (baked_index >= 0 && baked_index < BAKED_NUM_INDICES)
+       if (baked_index >= 0 && baked_index < getNumBakes())
        {
 		   return  getTexLayerSet(baked_index);
        }
@@ -3142,7 +3164,15 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const
     {
 		const ETextureIndex index = iter.first;
 		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter.second;
-		if (!texture_dict->mIsBakedTexture)
+		if( (index == TEX_SKIRT || index == TEX_SKIRT_TATTOO) && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT) )
+		{
+			// TODO(BEQ): combine this with clause below once proven it works.
+			LL_DEBUGS("Avatar") << "Ignoring skirt related texture at index=" << index << LL_ENDL;
+			LLTextureEntry* entry = getTE((U8) index);
+			texture_id[index] = entry->getID();
+			entry->setID(IMG_DEFAULT_AVATAR);
+		}
+		if (!texture_dict->mIsBakedTexture || index >= getRegion()->getRegionMaxTEs())
 		{
 			LLTextureEntry* entry = getTE((U8) index);
 			texture_id[index] = entry->getID();
@@ -3157,7 +3187,7 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const
     {
 		const ETextureIndex index = iter.first;
 		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter.second;
-		if (!texture_dict->mIsBakedTexture)
+		if (!texture_dict->mIsBakedTexture || index >= getRegion()->getRegionMaxTEs())
 		{
 			LLTextureEntry* entry = getTE((U8) index);
 			entry->setID(texture_id[index]);
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index 9820117a8e905c077425329e446ffaaf4d6f530b..f4a7d64cb4665f12263569e8e8c78ebdf08662e1 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -418,6 +418,7 @@ private:
 	F32 					mDebugTextureLoadTimes[LLAvatarAppearanceDefines::TEX_NUM_INDICES][MAX_DISCARD_LEVEL+1]; // load time for each texture at each discard level
 	F32 					mDebugBakedTextureTimes[LLAvatarAppearanceDefines::BAKED_NUM_INDICES][2]; // time to start upload and finish upload of each baked texture
 	void					debugTimingLocalTexLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+	void 					checkBOMRebakeRequired();
 
 // [SL:KB] - Patch: Appearance-TeleportAttachKill | Checked: Catznip-4.0
 //public: