diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
index 2cc741cffffb403b7644353ff368b85f22d13256..136e8d47d8c03afa501f3880989494c657aa84e2 100644
--- a/indra/llinventory/llinventory.cpp
+++ b/indra/llinventory/llinventory.cpp
@@ -897,155 +897,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
 		mAssetUUID.setNull();
 	}
 
-#if 0  // old implementation.  makes a LOT of temporary copies and LLSD::safe(impl) calls
-	std::string w;
-
-	w = INV_ITEM_ID_LABEL;
-	if (sd.has(w))
-	{
-		mUUID = sd[w];
-	}
-	w = INV_PARENT_ID_LABEL;
-	if (sd.has(w))
-	{
-		mParentUUID = sd[w];
-	}
-    mThumbnailUUID.setNull();
-    w = INV_THUMBNAIL_LABEL;
-    if (sd.has(w))
-    {
-        const LLSD &thumbnail_map = sd[w];
-        w = INV_ASSET_ID_LABEL;
-        if (thumbnail_map.has(w))
-        {
-            mThumbnailUUID = thumbnail_map[w];
-        }
-        /* Example:
-            <key> asset_id </key>
-            <uuid> acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 </uuid>
-            <key> perms </key>
-            <integer> 8 </integer>
-            <key>service</key>
-            <integer> 3 </integer>
-            <key>version</key>
-            <integer> 1 </key>
-        */
-    }
-    else
-    {
-        w = INV_THUMBNAIL_ID_LABEL;
-        if (sd.has(w))
-        {
-            mThumbnailUUID = sd[w].asUUID();
-        }
-    }
-	w = INV_PERMISSIONS_LABEL;
-	if (sd.has(w))
-	{
-		mPermissions = ll_permissions_from_sd(sd[w]);
-	}
-	w = INV_SALE_INFO_LABEL;
-	if (sd.has(w))
-	{
-		// Sale info used to contain next owner perm. It is now in
-		// the permissions. Thus, we read that out, and fix legacy
-		// objects. It's possible this op would fail, but it
-		// should pick up the vast majority of the tasks.
-		BOOL has_perm_mask = FALSE;
-		U32 perm_mask = 0;
-		if (!mSaleInfo.fromLLSD(sd[w], has_perm_mask, perm_mask))
-		{
-			goto fail;
-		}
-		if (has_perm_mask)
-		{
-			if(perm_mask == PERM_NONE)
-			{
-				perm_mask = mPermissions.getMaskOwner();
-			}
-			// fair use fix.
-			if(!(perm_mask & PERM_COPY))
-			{
-				perm_mask |= PERM_TRANSFER;
-			}
-			mPermissions.setMaskNext(perm_mask);
-		}
-	}
-	w = INV_SHADOW_ID_LABEL;
-	if (sd.has(w))
-	{
-		mAssetUUID = sd[w];
-		LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
-		cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
-	}
-	w = INV_ASSET_ID_LABEL;
-	if (sd.has(w))
-	{
-		mAssetUUID = sd[w];
-	}
-	w = INV_LINKED_ID_LABEL;
-	if (sd.has(w))
-	{
-		mAssetUUID = sd[w];
-	}
-	w = INV_ASSET_TYPE_LABEL;
-	if (sd.has(w))
-	{
-		if (sd[w].isString())
-		{
-			mType = LLAssetType::lookup(sd[w].asString().c_str());
-		}
-		else if (sd[w].isInteger())
-		{
-			S8 type = (U8)sd[w].asInteger();
-			mType = static_cast<LLAssetType::EType>(type);
-		}
-	}
-	w = INV_INVENTORY_TYPE_LABEL;
-	if (sd.has(w))
-	{
-		if (sd[w].isString())
-		{
-			mInventoryType = LLInventoryType::lookup(sd[w].asString().c_str());
-		}
-		else if (sd[w].isInteger())
-		{
-			S8 type = (U8)sd[w].asInteger();
-			mInventoryType = static_cast<LLInventoryType::EType>(type);
-		}
-	}
-	w = INV_FLAGS_LABEL;
-	if (sd.has(w))
-	{
-		if (sd[w].isBinary())
-		{
-			mFlags = ll_U32_from_sd(sd[w]);
-		}
-		else if(sd[w].isInteger())
-		{
-			mFlags = sd[w].asInteger();
-		}
-	}
-	w = INV_NAME_LABEL;
-	if (sd.has(w))
-	{
-		mName = sd[w].asString();
-		LLStringUtil::replaceNonstandardASCII(mName, ' ');
-		LLStringUtil::replaceChar(mName, '|', ' ');
-	}
-	w = INV_DESC_LABEL;
-	if (sd.has(w))
-	{
-		mDescription = sd[w].asString();
-		LLStringUtil::replaceNonstandardASCII(mDescription, ' ');
-	}
-	w = INV_CREATION_DATE_LABEL;
-	if (sd.has(w))
-	{
-		mCreationDate = sd[w].asInteger();
-	}
-#else  // if 0 - new implementation follows
-
+    // TODO - figure out if this should be moved into the noclobber fields above
     mThumbnailUUID.setNull();
 
     // iterate as map to avoid making unnecessary temp copies of everything
@@ -1056,11 +908,13 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
         if (i->first == INV_ITEM_ID_LABEL)
         {
             mUUID = i->second;
+            continue;
         }
 
         if (i->first == INV_PARENT_ID_LABEL)
         {
             mParentUUID = i->second;
+            continue;
         }
 
         if (i->first == INV_THUMBNAIL_LABEL)
@@ -1080,15 +934,18 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
                 <key>version</key>
                 <integer> 1 </key>
             */
-        }
+          continue;
+      }
         else if (i->first == INV_THUMBNAIL_ID_LABEL)
         {
             mThumbnailUUID = i->second.asUUID();
+            continue;
         }
 
         if (i->first == INV_PERMISSIONS_LABEL)
         {
             mPermissions = ll_permissions_from_sd(i->second);
+            continue;
         }
 
         if (i->first == INV_SALE_INFO_LABEL)
@@ -1101,7 +958,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
             U32  perm_mask     = 0;
             if (!mSaleInfo.fromLLSD(i->second, has_perm_mask, perm_mask))
             {
-                goto fail;
+                return false;
             }
             if (has_perm_mask)
             {
@@ -1116,6 +973,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
                 }
                 mPermissions.setMaskNext(perm_mask);
             }
+            continue;
         }
 
         if (i->first == INV_SHADOW_ID_LABEL)
@@ -1123,16 +981,19 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
             mAssetUUID = i->second;
             LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
             cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
+            continue;
         }
 
         if (i->first == INV_ASSET_ID_LABEL)
         {
             mAssetUUID = i->second;
+            continue;
         }
 
         if (i->first == INV_LINKED_ID_LABEL)
         {
             mAssetUUID = i->second;
+            continue;
         }
 
         if (i->first == INV_ASSET_TYPE_LABEL)
@@ -1147,6 +1008,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
                 S8 type = (U8) label.asInteger();
                 mType   = static_cast<LLAssetType::EType>(type);
             }
+            continue;
         }
 
         if (i->first == INV_INVENTORY_TYPE_LABEL)
@@ -1161,6 +1023,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
                 S8 type        = (U8) label.asInteger();
                 mInventoryType = static_cast<LLInventoryType::EType>(type);
             }
+            continue;
         }
 
         if (i->first == INV_FLAGS_LABEL)
@@ -1174,6 +1037,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
             {
                 mFlags = label.asInteger();
             }
+            continue;
         }
 
         if (i->first == INV_NAME_LABEL)
@@ -1181,20 +1045,22 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
             mName = i->second.asString();
             LLStringUtil::replaceNonstandardASCII(mName, ' ');
             LLStringUtil::replaceChar(mName, '|', ' ');
+            continue;
         }
 
         if (i->first == INV_DESC_LABEL)
         {
             mDescription = i->second.asString();
             LLStringUtil::replaceNonstandardASCII(mDescription, ' ');
+            continue;
         }
 
         if (i->first == INV_CREATION_DATE_LABEL)
         {
             mCreationDate = i->second.asInteger();
+            continue;
         }
     }
-#endif // new version
 
 	// Need to convert 1.0 simstate files to a useful inventory type
 	// and potentially deal with bad inventory tyes eg, a landmark
@@ -1209,9 +1075,6 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
 	mPermissions.initMasks(mInventoryType);
 
 	return true;
-fail:
-	return false;
-
 }
 
 ///----------------------------------------------------------------------------
@@ -1306,59 +1169,6 @@ void LLInventoryCategory::packMessage(LLMessageSystem* msg) const
 
 bool LLInventoryCategory::fromLLSD(const LLSD& sd)
 {
-#if 0  // old implementation.  makes a LOT of temporary copies and LLSD::safe(impl) calls
-    std::string w;
-
-    w = INV_FOLDER_ID_LABEL_WS;
-    if (sd.has(w))
-    {
-        mUUID = sd[w];
-    }
-    w = INV_PARENT_ID_LABEL;
-    if (sd.has(w))
-    {
-        mParentUUID = sd[w];
-    }
-    mThumbnailUUID.setNull();
-    w = INV_THUMBNAIL_LABEL;
-    if (sd.has(w))
-    {
-        const LLSD &thumbnail_map = sd[w];
-        w = INV_ASSET_ID_LABEL;
-        if (thumbnail_map.has(w))
-        {
-            mThumbnailUUID = thumbnail_map[w];
-        }
-    }
-    else
-    {
-        w = INV_THUMBNAIL_ID_LABEL;
-        if (sd.has(w))
-        {
-            mThumbnailUUID = sd[w];
-        }
-    }
-    w = INV_ASSET_TYPE_LABEL;
-    if (sd.has(w))
-    {
-        S8 type = (U8)sd[w].asInteger();
-        mPreferredType = static_cast<LLFolderType::EType>(type);
-    }
-	w = INV_ASSET_TYPE_LABEL_WS;
-	if (sd.has(w))
-	{
-		S8 type = (U8)sd[w].asInteger();
-        mPreferredType = static_cast<LLFolderType::EType>(type);
-	}
-
-    w = INV_NAME_LABEL;
-    if (sd.has(w))
-    {
-        mName = sd[w].asString();
-        LLStringUtil::replaceNonstandardASCII(mName, ' ');
-        LLStringUtil::replaceChar(mName, '|', ' ');
-    }
-#else  // if 0 - new implementation follows
 	mThumbnailUUID.setNull();
 
 	// iterate as map to avoid making unnecessary temp copies of everything
@@ -1369,11 +1179,13 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd)
 		if (i->first == INV_FOLDER_ID_LABEL_WS)
 		{
 			mUUID = i->second;
+			continue;
 		}
 
 		if (i->first == INV_PARENT_ID_LABEL)
 		{
 			mParentUUID = i->second;
+			continue;
 		}
 
 		if (i->first == INV_THUMBNAIL_LABEL)
@@ -1383,22 +1195,26 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd)
 			{
 				mThumbnailUUID = thumbnail_map[INV_ASSET_ID_LABEL];
 			}
+			continue;
 		}
 		else if (i->first == INV_THUMBNAIL_ID_LABEL)
 		{
 			mThumbnailUUID = i->second;
+			continue;
 		}
 
 		if (i->first == INV_ASSET_TYPE_LABEL)
 		{
 			S8 type = (U8)i->second.asInteger();
 			mPreferredType = static_cast<LLFolderType::EType>(type);
+			continue;
 		}
 
 		if (i->first == INV_ASSET_TYPE_LABEL_WS)
 		{
 			S8 type = (U8)i->second.asInteger();
 			mPreferredType = static_cast<LLFolderType::EType>(type);
+			continue;
 		}
 
 		if (i->first == INV_NAME_LABEL)
@@ -1406,9 +1222,10 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd)
 			mName = i->second.asString();
 			LLStringUtil::replaceNonstandardASCII(mName, ' ');
 			LLStringUtil::replaceChar(mName, '|', ' ');
+			continue;
 		}
 	}
-#endif
+
     return true;
 }
 
@@ -1549,41 +1366,6 @@ LLSD LLInventoryCategory::exportLLSD() const
 
 bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
 {
-#if 0
-	if (cat_data.has(INV_FOLDER_ID_LABEL))
-	{
-		setUUID(cat_data[INV_FOLDER_ID_LABEL].asUUID());
-	}
-	if (cat_data.has(INV_PARENT_ID_LABEL))
-	{
-		setParent(cat_data[INV_PARENT_ID_LABEL].asUUID());
-	}
-	if (cat_data.has(INV_ASSET_TYPE_LABEL))
-	{
-		setType(LLAssetType::lookup(cat_data[INV_ASSET_TYPE_LABEL].asString()));
-	}
-	if (cat_data.has(INV_PREFERRED_TYPE_LABEL))
-	{
-		setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString()));
-	}
-    if (cat_data.has(INV_THUMBNAIL_LABEL))
-    {
-        LLUUID thumbnail_uuid;
-        const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL];
-        if (thumbnail_data.has(INV_ASSET_ID_LABEL))
-        {
-            thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID();
-        }
-        setThumbnailUUID(thumbnail_uuid);
-    }
-	if (cat_data.has(INV_NAME_LABEL))
-	{
-		mName = cat_data[INV_NAME_LABEL].asString();
-		LLStringUtil::replaceNonstandardASCII(mName, ' ');
-		LLStringUtil::replaceChar(mName, '|', ' ');
-	}
-#else  // if 0 - new implementation follows
-
 	// iterate as map to avoid making unnecessary temp copies of everything
 	LLSD::map_const_iterator i, end;
 	end = cat_data.endMap();
@@ -1592,18 +1374,22 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
 		if (i->first == INV_FOLDER_ID_LABEL)
 		{
 			setUUID(i->second.asUUID());
+			continue;
 		}
 		if (i->first == INV_PARENT_ID_LABEL)
 		{
 			setParent(i->second.asUUID());
+			continue;
 		}
 		if (i->first == INV_ASSET_TYPE_LABEL)
 		{
 			setType(LLAssetType::lookup(i->second.asString()));
+			continue;
 		}
 		if (i->first == INV_PREFERRED_TYPE_LABEL)
 		{
 			setPreferredType(LLFolderType::lookup(i->second.asString()));
+			continue;
 		}
 		if (i->first == INV_THUMBNAIL_LABEL)
 		{
@@ -1614,15 +1400,16 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
 				thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID();
 			}
 			setThumbnailUUID(thumbnail_uuid);
+			continue;
 		}
 		if (i->first == INV_NAME_LABEL)
 		{
 			mName = i->second.asString();
 			LLStringUtil::replaceNonstandardASCII(mName, ' ');
 			LLStringUtil::replaceChar(mName, '|', ' ');
+			continue;
 		}
 	}
-#endif
 
 	return true;
 }
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp
index 4a2eea7ba69bee3406db96d22adbae1418e2821d..ec4cd070ef61553182b6b4f05b8b6e454d074002 100644
--- a/indra/llprimitive/llgltfmaterial.cpp
+++ b/indra/llprimitive/llgltfmaterial.cpp
@@ -95,6 +95,10 @@ LLGLTFMaterial& LLGLTFMaterial::operator=(const LLGLTFMaterial& rhs)
     mOverrideDoubleSided = rhs.mOverrideDoubleSided;
     mOverrideAlphaMode = rhs.mOverrideAlphaMode;
 
+    mTrackingIdToLocalTexture = rhs.mTrackingIdToLocalTexture;
+
+    updateTextureTracking();
+
     return *this;
 }
 
@@ -607,6 +611,10 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
             mTextureTransform[i].mRotation = override_mat.mTextureTransform[i].mRotation;
         }
     }
+
+    mTrackingIdToLocalTexture.insert(override_mat.mTrackingIdToLocalTexture.begin(), override_mat.mTrackingIdToLocalTexture.begin());
+
+    updateTextureTracking();
 }
 
 void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data)
@@ -771,3 +779,47 @@ LLUUID LLGLTFMaterial::getHash() const
     return hash;
 }
 
+void LLGLTFMaterial::addLocalTextureTracking(const LLUUID& tracking_id, const LLUUID& tex_id)
+{
+    mTrackingIdToLocalTexture[tracking_id] = tex_id;
+}
+
+void LLGLTFMaterial::removeLocalTextureTracking(const LLUUID& tracking_id)
+{
+    mTrackingIdToLocalTexture.erase(tracking_id);
+}
+
+bool LLGLTFMaterial::replaceLocalTexture(const LLUUID& tracking_id, const LLUUID& old_id, const LLUUID& new_id)
+{
+    bool res = false;
+
+    for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+    {
+        if (mTextureId[i] == old_id)
+        {
+            mTextureId[i] = new_id;
+            res = true;
+        }
+        else if (mTextureId[i] == new_id)
+        {
+            res = true;
+        }
+    }
+
+    if (res)
+    {
+        mTrackingIdToLocalTexture[tracking_id] = new_id;
+    }
+    else
+    {
+        mTrackingIdToLocalTexture.erase(tracking_id);
+    }
+
+    return res;
+}
+
+void LLGLTFMaterial::updateTextureTracking()
+{
+    // setTEGLTFMaterialOverride is responsible for tracking
+    // for material overrides editor will set it
+}
diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h
index 822a0aab229fa6d60284cba064cbd5a6e92a8ab9..02f62fb08c4bb78765cfe59fbc6040831ad40709 100644
--- a/indra/llprimitive/llgltfmaterial.h
+++ b/indra/llprimitive/llgltfmaterial.h
@@ -196,7 +196,7 @@ class LLGLTFMaterial : public LLRefCount
     // write to given tinygltf::Model
     void writeToModel(tinygltf::Model& model, S32 mat_index) const;
 
-    void applyOverride(const LLGLTFMaterial& override_mat);
+    virtual void applyOverride(const LLGLTFMaterial& override_mat);
     
     // apply the given LLSD override data
     void applyOverrideLLSD(const LLSD& data);
@@ -222,6 +222,17 @@ class LLGLTFMaterial : public LLRefCount
     virtual void addTextureEntry(LLTextureEntry* te) {};
     virtual void removeTextureEntry(LLTextureEntry* te) {};
 
+    // For local textures so that editor will know to track changes
+    void addLocalTextureTracking(const LLUUID& tracking_id, const LLUUID &tex_id);
+    void removeLocalTextureTracking(const LLUUID& tracking_id);
+    bool hasLocalTextures() { return !mTrackingIdToLocalTexture.empty(); }
+    virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID &old_id, const LLUUID& new_id);
+    virtual void updateTextureTracking();
+
+    // These fields are local to viewer and are a part of local bitmap support
+    typedef std::map<LLUUID, LLUUID> local_tex_map_t;
+    local_tex_map_t mTrackingIdToLocalTexture;
+
 protected:
     static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
     static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);
diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp
index fc9d42bfb6aec3cdfc00e94323d26a8be8026bb0..1a4729352349f85ae4d4b5387ab41c56de8b6b33 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -144,6 +144,83 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
 
 }
 
+LLViewerFetchedTexture* fetch_texture(const LLUUID& id)
+{
+    LLViewerFetchedTexture* img = nullptr;
+    if (id.notNull())
+    {
+        img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+        img->addTextureStats(64.f * 64.f, TRUE);
+    }
+    return img;
+};
+
+bool LLFetchedGLTFMaterial::replaceLocalTexture(const LLUUID& tracking_id, const LLUUID& old_id, const LLUUID& new_id)
+{
+    bool res = false;
+    if (mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] == old_id)
+    {
+        mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = new_id;
+        mBaseColorTexture = fetch_texture(new_id);
+        res = true;
+    }
+    if (mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] == old_id)
+    {
+        mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = new_id;
+        mNormalTexture = fetch_texture(new_id);
+        res = true;
+    }
+    if (mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] == old_id)
+    {
+        mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = new_id;
+        mMetallicRoughnessTexture = fetch_texture(new_id);
+        res = true;
+    }
+    if (mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] == old_id)
+    {
+        mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = new_id;
+        mEmissiveTexture = fetch_texture(new_id);
+        res = true;
+    }
+
+    for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+    {
+        if (mTextureId[i] == new_id)
+        {
+            res = true;
+        }
+    }
+
+    if (res)
+    {
+        mTrackingIdToLocalTexture[tracking_id] = new_id;
+    }
+    else
+    {
+        mTrackingIdToLocalTexture.erase(tracking_id);
+    }
+
+    return res;
+}
+
+void LLFetchedGLTFMaterial::addTextureEntry(LLTextureEntry* te)
+{
+    mTextureEntires.insert(te);
+}
+
+void LLFetchedGLTFMaterial::removeTextureEntry(LLTextureEntry* te)
+{
+    mTextureEntires.erase(te);
+}
+
+void LLFetchedGLTFMaterial::updateTextureTracking()
+{
+    for (local_tex_map_t::value_type &val : mTrackingIdToLocalTexture)
+    {
+        LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.first, this);
+    }
+}
+
 void LLFetchedGLTFMaterial::materialBegin()
 {
     llassert(!mFetching);
diff --git a/indra/newview/llfetchedgltfmaterial.h b/indra/newview/llfetchedgltfmaterial.h
index 166865728146f05a5b9ec4c1d8257eeeca4a7591..2559aa46cc37a1e1b0f9575c547c9f708cee9c04 100644
--- a/indra/newview/llfetchedgltfmaterial.h
+++ b/indra/newview/llfetchedgltfmaterial.h
@@ -50,12 +50,19 @@ class LLFetchedGLTFMaterial: public LLGLTFMaterial
 
     bool isFetching() const { return mFetching; }
 
+    void addTextureEntry(LLTextureEntry* te) override;
+    void removeTextureEntry(LLTextureEntry* te) override;
+    virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID& old_id, const LLUUID& new_id) override;
+    virtual void updateTextureTracking() override;
+
     // Textures used for fetching/rendering
     LLPointer<LLViewerFetchedTexture> mBaseColorTexture;
     LLPointer<LLViewerFetchedTexture> mNormalTexture;
     LLPointer<LLViewerFetchedTexture> mMetallicRoughnessTexture;
     LLPointer<LLViewerFetchedTexture> mEmissiveTexture;
 
+    std::set<LLTextureEntry*> mTextureEntires;
+
 protected:
     // Lifetime management
     
diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp
index eb73310a6ed94e21ee693854226a403e5e0341da..40c179e71269ee5c4bf50bbb5e7be676d8d57a12 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.cpp
+++ b/indra/newview/llfloaterchangeitemthumbnail.cpp
@@ -762,7 +762,7 @@ void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id)
         {
             //texture_floaterp->setTextureSelectedCallback();
             //texture_floaterp->setOnUpdateImageStatsCallback();
-            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&)
+            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&, const LLUUID&)
             {
                 if (op == LLTextureCtrl::TEXTURE_SELECT)
                 {
diff --git a/indra/newview/llfloatertexturepicker.cpp b/indra/newview/llfloatertexturepicker.cpp
index 0c8bae5b91b7adeea538d1e3cc83978d3d48b749..7d4a1661e6cdc88932431dc1d269a745a5d72c8b 100644
--- a/indra/newview/llfloatertexturepicker.cpp
+++ b/indra/newview/llfloatertexturepicker.cpp
@@ -767,6 +767,7 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
     }
     LLUUID asset_id = mImageAssetID;
     LLUUID inventory_id;
+    LLUUID tracking_id;
     LLPickerSource mode = (LLPickerSource)mModeSelector->getValue().asInteger();
 
     switch (mode)
@@ -807,16 +808,16 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
                 if (!mLocalScrollCtrl->getAllSelected().empty())
                 {
                     LLSD data = mLocalScrollCtrl->getFirstSelected()->getValue();
-                    LLUUID temp_id = data["id"];
+                    tracking_id = data["id"];
                     S32 asset_type = data["type"].asInteger();
 
                     if (LLAssetType::AT_MATERIAL == asset_type)
                     {
-                        asset_id = LLLocalGLTFMaterialMgr::getInstance()->getWorldID(temp_id);
+                        asset_id = LLLocalGLTFMaterialMgr::getInstance()->getWorldID(tracking_id);
                     }
                     else
                     {
-                        asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id);
+                        asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
                     }
                 }
                 else
@@ -833,13 +834,13 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
             break;
     }
 
-    mOnFloaterCommitCallback(op, mode, asset_id, inventory_id);
+    mOnFloaterCommitCallback(op, mode, asset_id, inventory_id, tracking_id);
 }
 void LLFloaterTexturePicker::commitCancel()
 {
 	if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply)
 	{
-		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null);
+		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null, LLUUID::null);
 	}
 }
 
@@ -902,7 +903,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata)
 	self->setImageID( self->mOriginalImageAssetID );
 	if (self->mOnFloaterCommitCallback)
 	{
-		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null);
+		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null, LLUUID::null);
 	}
 	self->mViewModel->resetDirty();
 	self->closeFloater();
@@ -1119,7 +1120,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
 		{
 			if (self->mOnFloaterCommitCallback)
 			{
-				self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null);
+				self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null, tracking_id);
 			}
 		}
 	}
diff --git a/indra/newview/llfloatertexturepicker.h b/indra/newview/llfloatertexturepicker.h
index a0fe4e2e38aabd774d3ebf6b3c8720eca66c9848..3657555f6c3ead7128eae5b3d4de922dcabd47bb 100644
--- a/indra/newview/llfloatertexturepicker.h
+++ b/indra/newview/llfloatertexturepicker.h
@@ -35,7 +35,7 @@
 class LLFilterEditor;
 class LLTabContainer;
 
-typedef std::function<void(LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inventory_id)> floater_commit_callback;
+typedef std::function<void(LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inventory_id, const LLUUID& tracking_id)> floater_commit_callback;
 typedef std::function<void()> floater_close_callback;
 typedef std::function<void(const LLUUID& asset_id)> set_image_asset_id_callback;
 typedef std::function<void(LLPointer<LLViewerTexture> texture)> set_on_update_image_stats_callback;
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index ffd3f53c1e0c671c832835b71b4b7583296903c3..1c3ee22ed6ab190c15d37ee548b77b035b0f46d3 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -3522,7 +3522,7 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,
 
 	is_cache_obsolete = true; // Obsolete until proven current
 
-	U64 lines_count = 0U;
+	//U64 lines_count = 0U;
 	std::string line;
 	LLPointer<LLSDParser> parser = new LLSDNotationParser();
 	while (std::getline(file, line)) 
@@ -3588,12 +3588,13 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,
 			}	
 		}
 
-		//static constexpr U64 BATCH_SIZE = 512U;
-		//if ((++lines_count % BATCH_SIZE) == 0)
-		//{
-		//	// SL-19968 - make sure message system code gets a chance to run every so often
-		//	pump_idle_startup_network();
-		//}
+//      TODO(brad) - figure out how to reenable this without breaking everything else
+//		static constexpr U64 BATCH_SIZE = 512U;
+//		if ((++lines_count % BATCH_SIZE) == 0)
+//		{
+//			// SL-19968 - make sure message system code gets a chance to run every so often
+//			pump_idle_startup_network();
+//		}
 	}
 
 	file.close();
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
index e1ab95b7f31a0aca49d50c8af3dcdb088e91b32f..6b8956ea352428c4c3ed5589eec4893e40404231 100644
--- a/indra/newview/lllocalbitmaps.cpp
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -47,6 +47,7 @@
 #include <ctime>
 
 /* misc headers */
+#include "llgltfmaterial.h"
 #include "llscrolllistctrl.h"
 #include "lllocaltextureobject.h"
 #include "llviewertexturelist.h"
@@ -136,6 +137,14 @@ LLLocalBitmap::~LLLocalBitmap()
 		LLLocalBitmapMgr::getInstance()->doRebake();
 	}
 
+    for (LLPointer<LLGLTFMaterial> &mat : mGLTFMaterialWithLocalTextures)
+    {
+        mat->removeLocalTextureTracking(getTrackingID());
+    }
+
+    mChangedSignal(getTrackingID(), getWorldID(), LLUUID());
+    mChangedSignal.disconnect_all_slots();
+
 	// delete self from gimagelist
 	LLViewerFetchedTexture* image = gTextureList.findImage(mWorldID, TEX_LIST_STANDARD);
 	gTextureList.deleteImage(image);
@@ -147,27 +156,27 @@ LLLocalBitmap::~LLLocalBitmap()
 }
 
 /* accessors */
-std::string LLLocalBitmap::getFilename()
+std::string LLLocalBitmap::getFilename() const
 {
 	return mFilename;
 }
 
-std::string LLLocalBitmap::getShortName()
+std::string LLLocalBitmap::getShortName() const
 {
 	return mShortName;
 }
 
-LLUUID LLLocalBitmap::getTrackingID()
+LLUUID LLLocalBitmap::getTrackingID() const
 {
 	return mTrackingID;
 }
 
-LLUUID LLLocalBitmap::getWorldID()
+LLUUID LLLocalBitmap::getWorldID() const
 {
 	return mWorldID;
 }
 
-bool LLLocalBitmap::getValid()
+bool LLLocalBitmap::getValid() const
 {
 	return mValid;
 }
@@ -284,6 +293,41 @@ bool LLLocalBitmap::updateSelf(EUpdateType optional_firstupdate)
 	return updated;
 }
 
+boost::signals2::connection LLLocalBitmap::setChangedCallback(const LLLocalTextureCallback& cb)
+{
+    return mChangedSignal.connect(cb);
+}
+
+void LLLocalBitmap::addGLTFMaterial(LLGLTFMaterial* mat)
+{
+    if (!mat)
+    {
+        return;
+    }
+
+    mat_list_t::iterator end = mGLTFMaterialWithLocalTextures.end();
+    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;)
+    {
+        if (it->get() == mat)
+        {
+            return;
+        }
+
+        if ((*it)->getNumRefs() == 1)
+        {
+            it = mGLTFMaterialWithLocalTextures.erase(it);
+            end = mGLTFMaterialWithLocalTextures.end();
+        }
+        else
+        {
+            it++;
+        }
+    }
+
+    mat->addLocalTextureTracking(getTrackingID(), getWorldID());
+    mGLTFMaterialWithLocalTextures.push_back(mat);
+}
+
 bool LLLocalBitmap::decodeBitmap(LLPointer<LLImageRaw> rawimg)
 {
 	bool decode_successful = false;
@@ -362,7 +406,7 @@ bool LLLocalBitmap::decodeBitmap(LLPointer<LLImageRaw> rawimg)
 	return decode_successful;
 }
 
-void LLLocalBitmap::replaceIDs(LLUUID old_id, LLUUID new_id)
+void LLLocalBitmap::replaceIDs(const LLUUID& old_id, LLUUID new_id)
 {
 	// checking for misuse.
 	if (old_id == new_id)
@@ -372,6 +416,8 @@ void LLLocalBitmap::replaceIDs(LLUUID old_id, LLUUID new_id)
 		return;
 	}
 
+    mChangedSignal(getTrackingID(), old_id, new_id);
+
 	// processing updates per channel; makes the process scalable.
 	// the only actual difference is in SetTE* call i.e. SetTETexture, SetTENormal, etc.
 	updateUserPrims(old_id, new_id, LLRender::DIFFUSE_MAP);
@@ -403,6 +449,8 @@ void LLLocalBitmap::replaceIDs(LLUUID old_id, LLUUID new_id)
 	updateUserLayers(old_id, new_id, LLWearableType::WT_UNIVERSAL);
 	updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERPANTS);
 	updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERSHIRT);
+
+    updateGLTFMaterials(old_id, new_id);
 }
 
 // this function sorts the faces from a getFaceList[getNumFaces] into a list of objects
@@ -600,6 +648,67 @@ void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableTyp
 	}
 }
 
+void LLLocalBitmap::updateGLTFMaterials(LLUUID old_id, LLUUID new_id)
+{
+    // Might be a better idea to hold this in LLGLTFMaterialList
+    mat_list_t::iterator end = mGLTFMaterialWithLocalTextures.end();
+    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;)
+    {
+        if ((*it)->getNumRefs() == 1)
+        {
+            // render and override materials are often recreated,
+            // clean up any remains
+            it = mGLTFMaterialWithLocalTextures.erase(it);
+            end = mGLTFMaterialWithLocalTextures.end();
+        }
+        else if ((*it)->replaceLocalTexture(mTrackingID, old_id, new_id))
+        {
+            it++;
+        }
+        else
+        {
+            // Matching id not found, no longer in use
+            // material would clean itself, remove from the list
+            it = mGLTFMaterialWithLocalTextures.erase(it);
+            end = mGLTFMaterialWithLocalTextures.end();
+        }
+    }
+
+    // Render material consists of base and override materials, make sure replaceLocalTexture
+    // gets called for base and override before applyOverride
+    end = mGLTFMaterialWithLocalTextures.end();
+    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;)
+    {
+        LLFetchedGLTFMaterial* fetched_mat = dynamic_cast<LLFetchedGLTFMaterial*>((*it).get());
+        if (fetched_mat)
+        {
+            for (LLTextureEntry* entry : fetched_mat->mTextureEntires)
+            {
+                // Normally a change in applied material id is supposed to
+                // drop overrides thus reset material, but local materials
+                // currently reuse their existing asset id, and purpose is
+                // to preview how material will work in-world, overrides
+                // included, so do an override to render update instead.
+                LLGLTFMaterial* override_mat = entry->getGLTFMaterialOverride();
+                if (override_mat)
+                {
+                    // do not create a new material, reuse existing pointer
+                    LLFetchedGLTFMaterial* render_mat = (LLFetchedGLTFMaterial*)entry->getGLTFRenderMaterial();
+                    if (render_mat)
+                    {
+                        llassert(dynamic_cast<LLFetchedGLTFMaterial*>(entry->getGLTFRenderMaterial()) != nullptr);
+                        {
+                            *render_mat = *fetched_mat;
+                        }
+                        render_mat->applyOverride(*override_mat);
+                    }
+                }
+            }
+        }
+        ++it;
+    }
+}
+
 LLAvatarAppearanceDefines::ETextureIndex LLLocalBitmap::getTexIndex(
 	LLWearableType::EType type, LLAvatarAppearanceDefines::EBakedTextureIndex baked_texind)
 {
@@ -1063,11 +1172,11 @@ void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
 	}
 }
 
-LLUUID LLLocalBitmapMgr::getWorldID(LLUUID tracking_id)
+LLUUID LLLocalBitmapMgr::getWorldID(const LLUUID &tracking_id) const
 {
 	LLUUID world_id = LLUUID::null;
 
-	for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
+	for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
 	{
 		LLLocalBitmap* unit = *iter;
 		if (unit->getTrackingID() == tracking_id)
@@ -1079,9 +1188,9 @@ LLUUID LLLocalBitmapMgr::getWorldID(LLUUID tracking_id)
 	return world_id;
 }
 
-bool LLLocalBitmapMgr::isLocal(const LLUUID world_id)
+bool LLLocalBitmapMgr::isLocal(const LLUUID &world_id) const
 {
-    for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
+    for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
     {
         LLLocalBitmap* unit = *iter;
         if (unit->getWorldID() == world_id)
@@ -1092,11 +1201,11 @@ bool LLLocalBitmapMgr::isLocal(const LLUUID world_id)
     return false;
 }
 
-std::string LLLocalBitmapMgr::getFilename(LLUUID tracking_id)
+std::string LLLocalBitmapMgr::getFilename(const LLUUID &tracking_id) const
 {
 	std::string filename = "";
 
-	for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
+	for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
 	{
 		LLLocalBitmap* unit = *iter;
 		if (unit->getTrackingID() == tracking_id)
@@ -1108,6 +1217,32 @@ std::string LLLocalBitmapMgr::getFilename(LLUUID tracking_id)
 	return filename;
 }
 
+boost::signals2::connection LLLocalBitmapMgr::setOnChangedCallback(const LLUUID tracking_id, const LLLocalBitmap::LLLocalTextureCallback &cb)
+{
+    for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
+    {
+        LLLocalBitmap* unit = *iter;
+        if (unit->getTrackingID() == tracking_id)
+        {
+            return unit->setChangedCallback(cb);
+        }
+    }
+
+    return boost::signals2::connection();
+}
+
+void LLLocalBitmapMgr::associateGLTFMaterial(const LLUUID tracking_id, LLGLTFMaterial* mat)
+{
+    for (local_list_iter iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
+    {
+        LLLocalBitmap* unit = *iter;
+        if (unit->getTrackingID() == tracking_id)
+        {
+            unit->addGLTFMaterial(mat);
+        }
+    }
+}
+
 void LLLocalBitmapMgr::feedScrollList(LLScrollListCtrl* ctrl)
 {
 	if (ctrl)
diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h
index c6ada6ac878db614e7b54498050dcbf71ffc48ac..9c89fdc7d9538ea02b0b868807e86bff0426ee88 100644
--- a/indra/newview/lllocalbitmaps.h
+++ b/indra/newview/lllocalbitmaps.h
@@ -36,6 +36,7 @@
 class LLScrollListCtrl;
 class LLImageRaw;
 class LLViewerObject;
+class LLGLTFMaterial;
 
 class LLLocalBitmap
 {
@@ -44,11 +45,11 @@ class LLLocalBitmap
 		~LLLocalBitmap();
 
 	public: /* accessors */
-		std::string	getFilename();
-		std::string	getShortName();
-		LLUUID		getTrackingID();
-		LLUUID		getWorldID();
-		bool		getValid();
+		std::string	getFilename() const;
+		std::string	getShortName() const;
+		LLUUID		getTrackingID() const;
+		LLUUID		getWorldID() const;
+		bool		getValid() const;
 
 	public: /* self update public section */
 		enum EUpdateType
@@ -59,13 +60,21 @@ class LLLocalBitmap
 
 		bool updateSelf(EUpdateType = UT_REGUPDATE);
 
+        typedef boost::signals2::signal<void(const LLUUID& tracking_id,
+                                             const LLUUID& old_id,
+                                             const LLUUID& new_id)> LLLocalTextureChangedSignal;
+        typedef LLLocalTextureChangedSignal::slot_type LLLocalTextureCallback;
+        boost::signals2::connection setChangedCallback(const LLLocalTextureCallback& cb);
+        void addGLTFMaterial(LLGLTFMaterial* mat);
+
 	private: /* self update private section */
 		bool decodeBitmap(LLPointer<LLImageRaw> raw);
-		void replaceIDs(LLUUID old_id, LLUUID new_id);
+        void replaceIDs(const LLUUID &old_id, LLUUID new_id);
 		std::vector<LLViewerObject*> prepUpdateObjects(LLUUID old_id, U32 channel);
 		void updateUserPrims(LLUUID old_id, LLUUID new_id, U32 channel);
 		void updateUserVolumes(LLUUID old_id, LLUUID new_id, U32 channel);
 		void updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type);
+        void updateGLTFMaterials(LLUUID old_id, LLUUID new_id);
 		LLAvatarAppearanceDefines::ETextureIndex getTexIndex(LLWearableType::EType type, LLAvatarAppearanceDefines::EBakedTextureIndex baked_texind);
 
 	private: /* private enums */
@@ -94,6 +103,12 @@ class LLLocalBitmap
 		EExtension  mExtension;
 		ELinkStatus mLinkStatus;
 		S32         mUpdateRetries;
+        LLLocalTextureChangedSignal	mChangedSignal;
+
+        // Store a list of accosiated materials
+        // Might be a better idea to hold this in LLGLTFMaterialList
+        typedef std::vector<LLPointer<LLGLTFMaterial> > mat_list_t;
+        mat_list_t mGLTFMaterialWithLocalTextures;
 
 };
 
@@ -126,10 +141,12 @@ class LLLocalBitmapMgr final : public LLSingleton<LLLocalBitmapMgr>
 	void         delUnit(LLUUID tracking_id);
 	bool 		checkTextureDimensions(std::string filename);
 
-	LLUUID       getWorldID(LLUUID tracking_id);
-    bool         isLocal(LLUUID world_id);
-	std::string  getFilename(LLUUID tracking_id);
-    
+	LLUUID       getWorldID(const LLUUID &tracking_id) const;
+    bool         isLocal(const LLUUID& world_id) const;
+	std::string  getFilename(const LLUUID &tracking_id) const;
+    boost::signals2::connection setOnChangedCallback(const LLUUID tracking_id, const LLLocalBitmap::LLLocalTextureCallback& cb);
+    void associateGLTFMaterial(const LLUUID tracking_id, LLGLTFMaterial* mat);
+
 	void         feedScrollList(LLScrollListCtrl* ctrl);
 	void         doUpdates();
 	void         setNeedsRebake();
@@ -140,6 +157,7 @@ class LLLocalBitmapMgr final : public LLSingleton<LLLocalBitmapMgr>
 	LLLocalBitmapTimer           mTimer;
 	bool                         mNeedsRebake;
 	typedef std::list<LLLocalBitmap*>::iterator local_list_iter;
+    typedef std::list<LLLocalBitmap*>::const_iterator local_list_citer;
 };
 
 #endif
diff --git a/indra/newview/lllocalgltfmaterials.cpp b/indra/newview/lllocalgltfmaterials.cpp
index a9e1ee63784f716493de9829775aa6b16c26087a..7aaa70aaa68d1e7ee657d4f98ee039317bde6d64 100644
--- a/indra/newview/lllocalgltfmaterials.cpp
+++ b/indra/newview/lllocalgltfmaterials.cpp
@@ -119,15 +119,6 @@ S32 LLLocalGLTFMaterial::getIndexInFile() const
     return mMaterialIndex;
 }
 
-void LLLocalGLTFMaterial::addTextureEntry(LLTextureEntry* te)
-{
-    mTextureEntires.insert(te);
-}
-void LLLocalGLTFMaterial::removeTextureEntry(LLTextureEntry* te)
-{
-    mTextureEntires.erase(te);
-}
-
 /* update functions */
 bool LLLocalGLTFMaterial::updateSelf()
 {
diff --git a/indra/newview/lllocalgltfmaterials.h b/indra/newview/lllocalgltfmaterials.h
index f0cffe117aecca98bc27585762b34bfdc0aa0c7a..67124808557404012a837439de5e887415b10456 100644
--- a/indra/newview/lllocalgltfmaterials.h
+++ b/indra/newview/lllocalgltfmaterials.h
@@ -49,9 +49,6 @@ class LLLocalGLTFMaterial : public LLFetchedGLTFMaterial
     LLUUID		getWorldID() const;
     S32			getIndexInFile() const;
 
-    void addTextureEntry(LLTextureEntry* te) override;
-    void removeTextureEntry(LLTextureEntry* te) override;
-
 public:
     bool updateSelf();
 
@@ -81,7 +78,6 @@ class LLLocalGLTFMaterial : public LLFetchedGLTFMaterial
     ELinkStatus mLinkStatus;
     S32         mUpdateRetries;
     S32         mMaterialIndex; // Single file can have more than one
-    std::set<LLTextureEntry*> mTextureEntires;
 };
 
 class LLLocalGLTFMaterialTimer : public LLEventTimer
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 5b73c5f46281a653feb94077372d95b9a39cf666..d3025007f9eb606a2d824ab159edef3b747bcb00 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -350,6 +350,39 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index)
     return false;
 }
 
+class LLSelectedTEUpdateOverrides: public LLSelectedNodeFunctor
+{
+public:
+    LLSelectedTEUpdateOverrides(LLMaterialEditor* me) : mEditor(me) {}
+
+    virtual bool apply(LLSelectNode* nodep);
+
+    LLMaterialEditor* mEditor;
+};
+
+bool LLSelectedTEUpdateOverrides::apply(LLSelectNode* nodep)
+{
+    LLViewerObject* objectp = nodep->getObject();
+    if (!objectp)
+    {
+        return false;
+    }
+    S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); // avatars have TEs but no faces
+    for (S32 te_index = 0; te_index < num_tes; ++te_index)
+    {
+
+        LLTextureEntry* tep = objectp->getTE(te_index);
+        LLGLTFMaterial* override_mat = tep->getGLTFMaterialOverride();
+        if (mEditor->updateMaterialLocalSubscription(override_mat))
+        {
+            LLGLTFMaterial* render_mat = tep->getGLTFRenderMaterial();
+            mEditor->updateMaterialLocalSubscription(render_mat);
+        }
+    }
+
+    return true;
+}
+
 ///----------------------------------------------------------------------------
 /// Class LLMaterialEditor
 ///----------------------------------------------------------------------------
@@ -370,6 +403,10 @@ LLMaterialEditor::LLMaterialEditor(const LLSD& key)
     }
 }
 
+LLMaterialEditor::~LLMaterialEditor()
+{
+}
+
 void LLMaterialEditor::setObjectID(const LLUUID& object_id)
 {
     LLPreview::setObjectID(object_id);
@@ -538,6 +575,11 @@ void LLMaterialEditor::onClose(bool app_quitting)
     {
         mSelectionUpdateSlot.disconnect();
     }
+    for (mat_connection_map_t::value_type &cn : mTextureChangesUpdates)
+    {
+        cn.second.mConnection.disconnect();
+    }
+    mTextureChangesUpdates.clear();
 
     LLPreview::onClose(app_quitting);
 }
@@ -868,6 +910,118 @@ void LLMaterialEditor::setEnableEditing(bool can_modify)
     mNormalTextureCtrl->setEnabled(can_modify);
 }
 
+void LLMaterialEditor::subscribeToLocalTexture(S32 dirty_flag, const LLUUID& tracking_id)
+{
+    if (mTextureChangesUpdates[dirty_flag].mTrackingId != tracking_id)
+    {
+        mTextureChangesUpdates[dirty_flag].mConnection.disconnect();
+        mTextureChangesUpdates[dirty_flag].mTrackingId = tracking_id;
+        mTextureChangesUpdates[dirty_flag].mConnection = LLLocalBitmapMgr::getInstance()->setOnChangedCallback(tracking_id,
+                                                                                                               [this, dirty_flag](const LLUUID& tracking_id, const LLUUID& old_id, const LLUUID& new_id)
+                                                                                                               {
+                                                                                                                   if (new_id.isNull())
+                                                                                                                   {
+                                                                                                                       mTextureChangesUpdates[dirty_flag].mConnection.disconnect();
+                                                                                                                       //mTextureChangesUpdates.erase(dirty_flag);
+                                                                                                                   }
+                                                                                                                   else
+                                                                                                                   {
+                                                                                                                       replaceLocalTexture(old_id, new_id);
+                                                                                                                   }
+                                                                                                               });
+    }
+}
+
+LLUUID LLMaterialEditor::getLocalTextureTrackingIdFromFlag(U32 flag)
+{
+    mat_connection_map_t::iterator found = mTextureChangesUpdates.find(flag);
+    if (found != mTextureChangesUpdates.end())
+    {
+        return found->second.mTrackingId;
+    }
+    return LLUUID();
+}
+
+bool LLMaterialEditor::updateMaterialLocalSubscription(LLGLTFMaterial* mat)
+{
+    if (!mat)
+    {
+        return false;
+    }
+
+    bool res = false;
+    for (mat_connection_map_t::value_type& cn : mTextureChangesUpdates)
+    {
+        LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(cn.second.mTrackingId);
+        if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR])
+        {
+            LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(cn.second.mTrackingId, mat);
+            res = true;
+            continue;
+        }
+        if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS])
+        {
+            LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(cn.second.mTrackingId, mat);
+            res = true;
+            continue;
+        }
+        if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE])
+        {
+            LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(cn.second.mTrackingId, mat);
+            res = true;
+            continue;
+        }
+        if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL])
+        {
+            LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(cn.second.mTrackingId, mat);
+            res = true;
+            continue;
+        }
+    }
+    return res;
+}
+
+void LLMaterialEditor::replaceLocalTexture(const LLUUID& old_id, const LLUUID& new_id)
+{
+    // todo: might be a good idea to set mBaseColorTextureUploadId here
+    // and when texturectrl picks a local texture
+    if (getBaseColorId() == old_id)
+    {
+        mBaseColorTextureCtrl->setValue(new_id);
+    }
+    if (mBaseColorTextureCtrl->getDefaultImageAssetID() == old_id)
+    {
+        mBaseColorTextureCtrl->setDefaultImageAssetID(new_id);
+    }
+
+    if (getMetallicRoughnessId() == old_id)
+    {
+        mMetallicTextureCtrl->setValue(new_id);
+    }
+    if (mMetallicTextureCtrl->getDefaultImageAssetID() == old_id)
+    {
+        mMetallicTextureCtrl->setDefaultImageAssetID(new_id);
+    }
+
+    if (getEmissiveId() == old_id)
+    {
+        mEmissiveTextureCtrl->setValue(new_id);
+    }
+    if (mEmissiveTextureCtrl->getDefaultImageAssetID() == old_id)
+    {
+        mEmissiveTextureCtrl->setDefaultImageAssetID(new_id);
+    }
+
+    if (getNormalId() == old_id)
+    {
+        mNormalTextureCtrl->setValue(new_id);
+    }
+    if (mNormalTextureCtrl->getDefaultImageAssetID() == old_id)
+    {
+        mNormalTextureCtrl->setDefaultImageAssetID(new_id);
+    }
+}
+
 void LLMaterialEditor::onCommitTexture(LLUICtrl* ctrl, const LLSD& data, S32 dirty_flag)
 {
     if (!mIsOverride)
@@ -920,6 +1074,21 @@ void LLMaterialEditor::onCommitTexture(LLUICtrl* ctrl, const LLSD& data, S32 dir
         }
     }
 
+    LLTextureCtrl* tex_ctrl = (LLTextureCtrl*)ctrl;
+    if (tex_ctrl->isImageLocal())
+    {
+        subscribeToLocalTexture(dirty_flag, tex_ctrl->getLocalTrackingID());
+    }
+    else
+    {
+        // unsubcribe potential old callabck
+        mat_connection_map_t::iterator found = mTextureChangesUpdates.find(dirty_flag);
+        if (found != mTextureChangesUpdates.end())
+        {
+            found->second.mConnection.disconnect();
+        }
+    }
+
     markChangesUnsaved(dirty_flag);
     applyToSelection();
 }
@@ -930,6 +1099,16 @@ void LLMaterialEditor::onCancelCtrl(LLUICtrl* ctrl, const LLSD& data, S32 dirty_
     applyToSelection();
 }
 
+void update_local_texture(LLUICtrl* ctrl, LLGLTFMaterial* mat)
+{
+    LLTextureCtrl* tex_ctrl = (LLTextureCtrl*)ctrl;
+    if (tex_ctrl->isImageLocal())
+    {
+        // subscrive material to updates of local textures
+        LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tex_ctrl->getLocalTrackingID(), mat);
+    }
+}
+
 void LLMaterialEditor::onSelectCtrl(LLUICtrl* ctrl, const LLSD& data, S32 dirty_flag)
 {
     mUnsavedChanges |= dirty_flag;
@@ -965,21 +1144,25 @@ void LLMaterialEditor::onSelectCtrl(LLUICtrl* ctrl, const LLSD& data, S32 dirty_
                     case MATERIAL_BASE_COLOR_TEX_DIRTY:
                     {
                         nodep->mSavedGLTFOverrideMaterials[te]->setBaseColorId(mCtrl->getValue().asUUID(), true);
+                        update_local_texture(mCtrl, nodep->mSavedGLTFOverrideMaterials[te].get());
                         break;
                     }
                     case MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY:
                     {
                         nodep->mSavedGLTFOverrideMaterials[te]->setOcclusionRoughnessMetallicId(mCtrl->getValue().asUUID(), true);
+                        update_local_texture(mCtrl, nodep->mSavedGLTFOverrideMaterials[te].get());
                         break;
                     }
                     case MATERIAL_EMISIVE_TEX_DIRTY:
                     {
                         nodep->mSavedGLTFOverrideMaterials[te]->setEmissiveId(mCtrl->getValue().asUUID(), true);
+                        update_local_texture(mCtrl, nodep->mSavedGLTFOverrideMaterials[te].get());
                         break;
                     }
                     case MATERIAL_NORMAL_TEX_DIRTY:
                     {
                         nodep->mSavedGLTFOverrideMaterials[te]->setNormalId(mCtrl->getValue().asUUID(), true);
+                        update_local_texture(mCtrl, nodep->mSavedGLTFOverrideMaterials[te].get());
                         break;
                     }
                     // Colors
@@ -1397,6 +1580,20 @@ void LLMaterialEditor::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, L
         {
             me->refreshFromInventory(itemId);
         }
+
+        if (me && !me->mTextureChangesUpdates.empty())
+        {
+            const LLInventoryItem* item = me->getItem();
+            if (item)
+            {
+                // local materials were assigned, force load material and init tracking
+                LLGLTFMaterial* mat = gGLTFMaterialList.getMaterial(item->getAssetUUID());
+                for (mat_connection_map_t::value_type &val : me->mTextureChangesUpdates)
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.second.mTrackingId, mat);
+                }
+            }
+        }
     }
 }
 
@@ -1411,6 +1608,16 @@ void LLMaterialEditor::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUID
         me->setAssetId(newAssetId);
         me->refreshFromInventory();
         me->setEnabled(true);
+
+        if (me && !me->mTextureChangesUpdates.empty())
+        {
+            // local materials were assigned, force load material and init tracking
+            LLGLTFMaterial* mat = gGLTFMaterialList.getMaterial(newAssetId);
+            for (mat_connection_map_t::value_type &val : me->mTextureChangesUpdates)
+            {
+                LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.second.mTrackingId, mat);
+            }
+        }
     }
 }
 
@@ -1444,6 +1651,17 @@ void LLMaterialEditor::finishSaveAs(
             {
                 me->loadAsset();
                 me->setEnabled(true);
+
+                // Local texure support
+                if (!me->mTextureChangesUpdates.empty())
+                {
+                    // local materials were assigned, force load material and init tracking
+                    LLGLTFMaterial* mat = gGLTFMaterialList.getMaterial(item->getAssetUUID());
+                    for (mat_connection_map_t::value_type &val : me->mTextureChangesUpdates)
+                    {
+                        LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.second.mTrackingId, mat);
+                    }
+                }
             }
         }
         else if(has_unsaved_changes)
@@ -2698,28 +2916,58 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedNodeFunctor
             if (changed_flags & MATERIAL_BASE_COLOR_TEX_DIRTY)
             {
                 material->setBaseColorId(mEditor->getBaseColorId(), true);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_BASE_COLOR_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
             else if ((reverted_flags & MATERIAL_BASE_COLOR_TEX_DIRTY) && revert_mat.notNull())
             {
                 material->setBaseColorId(revert_mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR], false);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_BASE_COLOR_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
 
             if (changed_flags & MATERIAL_NORMAL_TEX_DIRTY)
             {
                 material->setNormalId(mEditor->getNormalId(), true);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_NORMAL_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
             else if ((reverted_flags & MATERIAL_NORMAL_TEX_DIRTY) && revert_mat.notNull())
             {
                 material->setNormalId(revert_mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL], false);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_NORMAL_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
 
             if (changed_flags & MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)
             {
                 material->setOcclusionRoughnessMetallicId(mEditor->getMetallicRoughnessId(), true);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
             else if ((reverted_flags & MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY) && revert_mat.notNull())
             {
                 material->setOcclusionRoughnessMetallicId(revert_mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS], false);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
 
             if (changed_flags & MATERIAL_METALLIC_ROUGHTNESS_METALNESS_DIRTY)
@@ -2752,10 +3000,20 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedNodeFunctor
             if (changed_flags & MATERIAL_EMISIVE_TEX_DIRTY)
             {
                 material->setEmissiveId(mEditor->getEmissiveId(), true);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_EMISIVE_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
             else if ((reverted_flags & MATERIAL_EMISIVE_TEX_DIRTY) && revert_mat.notNull())
             {
                 material->setEmissiveId(revert_mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE], false);
+                LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_EMISIVE_TEX_DIRTY);
+                if (tracking_id.notNull())
+                {
+                    LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material);
+                }
             }
 
             if (changed_flags & MATERIAL_DOUBLE_SIDED_DIRTY)
@@ -2907,6 +3165,34 @@ void LLMaterialEditor::setFromGLTFMaterial(LLGLTFMaterial* mat)
     setDoubleSided(mat->mDoubleSided);
     setAlphaMode(mat->getAlphaMode());
     setAlphaCutoff(mat->mAlphaCutoff);
+
+    if (mat->hasLocalTextures())
+    {
+        for (LLGLTFMaterial::local_tex_map_t::value_type &val : mat->mTrackingIdToLocalTexture)
+        {
+            LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(val.first);
+            if (val.second != world_id)
+            {
+                LL_WARNS() << "world id mismatch" << LL_ENDL;
+            }
+            if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR])
+            {
+                subscribeToLocalTexture(MATERIAL_BASE_COLOR_TEX_DIRTY, val.first);
+            }
+            if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS])
+            {
+                subscribeToLocalTexture(MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY, val.first);
+            }
+            if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE])
+            {
+                subscribeToLocalTexture(MATERIAL_EMISIVE_TEX_DIRTY, val.first);
+            }
+            if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL])
+            {
+                subscribeToLocalTexture(MATERIAL_NORMAL_TEX_DIRTY, val.first);
+            }
+        }
+    }
 }
 
 bool LLMaterialEditor::setFromSelection()
@@ -2925,6 +3211,8 @@ bool LLMaterialEditor::setFromSelection()
         const LLViewerInventoryItem* item = selected_object->getInventoryItemByAsset(func.mMaterialId);
         const bool allow_modify = !item || canModify(selected_object, item);
         setEnableEditing(allow_modify);
+
+        // todo: apply local texture data to all materials in selection
     }
     else
     {
@@ -2947,6 +3235,15 @@ bool LLMaterialEditor::setFromSelection()
         // Memorize selection data for filtering further updates
         mOverrideObjectId = func.mObjectId;
         mOverrideObjectTE = func.mObjectTE;
+
+        // Ovverdired might have been updated,
+        // refresh state of local textures in overrides
+        // 
+        // Todo: this probably shouldn't be here, but in localbitmap,
+        // subscried to all material overrides if we want copied
+        // objects to get properly updated as well
+        LLSelectedTEUpdateOverrides local_tex_func(this);
+        selected_objects->applyToNodes(&local_tex_func);
     }
 
     return func.mMaterial.notNull();
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index 1c40fcc348f29c1a8c60cf05f4ff95be09337a80..95a4c4572dd73a89f776eb40c76f3d7ee076a35b 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -87,6 +87,7 @@ class LLFloaterComboOptions : public LLFloater
 class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
 { public:
 	LLMaterialEditor(const LLSD& key);
+    ~LLMaterialEditor();
 
     bool setFromGltfModel(const tinygltf::Model& model, S32 index, bool set_textures = false);
 
@@ -219,6 +220,8 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     void setCanSave(bool value);
     void setEnableEditing(bool can_modify);
 
+    void subscribeToLocalTexture(S32 dirty_flag, const LLUUID& tracking_id);
+    void replaceLocalTexture(const LLUUID& old_id, const LLUUID& new_id); // Local texture support
     void onCommitTexture(LLUICtrl* ctrl, const LLSD& data, S32 dirty_flag);
     void onCancelCtrl(LLUICtrl* ctrl, const LLSD& data, S32 dirty_flag);
     void onSelectCtrl(LLUICtrl* ctrl, const LLSD& data, S32 dirty_flag);
@@ -228,6 +231,8 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
 
     U32 getUnsavedChangesFlags() { return mUnsavedChanges; }
     U32 getRevertedChangesFlags() { return mRevertedChanges; }
+    LLUUID getLocalTextureTrackingIdFromFlag(U32 flag);
+    bool updateMaterialLocalSubscription(LLGLTFMaterial* mat);
 
     static bool capabilitiesAvailable();
 
@@ -306,5 +311,13 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     static bool mOverrideInProgress;
     static bool mSelectionNeedsUpdate;
     boost::signals2::connection mSelectionUpdateSlot;
+
+    struct LocalTextureConnection
+    {
+        LLUUID mTrackingId;
+        boost::signals2::connection mConnection;
+    };
+    typedef std::map<S32, LocalTextureConnection> mat_connection_map_t;
+    mat_connection_map_t mTextureChangesUpdates;
 };
 
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index e3f9be6cd97ccaf0c4835eef1f429c01820bc7e0..ef787881261c2dc92538431879742b272a55bd29 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1594,7 +1594,7 @@ void LLPanelProfileSecondLife::onShowTexturePicker()
 
             mFloaterTexturePickerHandle = texture_floaterp->getHandle();
 
-            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&)
+            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, const LLUUID&)
             {
                 if (op == LLTextureCtrl::TEXTURE_SELECT)
                 {
@@ -1920,7 +1920,7 @@ void LLPanelProfileFirstLife::onChangePhoto()
 
             mFloaterTexturePickerHandle = texture_floaterp->getHandle();
 
-            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&)
+            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, const LLUUID&)
             {
                 if (op == LLTextureCtrl::TEXTURE_SELECT)
                 {
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 68b69c18aeeea1ffb5bb06d1b6c6201099571470..5b38e6682e83c27320b918e851a1bebb573cc50e 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -836,6 +836,7 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
     }
     LLUUID asset_id = mImageAssetID;
     LLUUID inventory_id;
+    LLUUID tracking_id;
     LLPickerSource mode = (LLPickerSource)mModeSelector->getValue().asInteger();
 
     switch (mode)
@@ -876,16 +877,16 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
                 if (!mLocalScrollCtrl->getAllSelected().empty())
                 {
                     LLSD data = mLocalScrollCtrl->getFirstSelected()->getValue();
-                    LLUUID temp_id = data["id"];
+                    tracking_id = data["id"];
                     S32 asset_type = data["type"].asInteger();
 
                     if (LLAssetType::AT_MATERIAL == asset_type)
                     {
-                        asset_id = LLLocalGLTFMaterialMgr::getInstance()->getWorldID(temp_id);
+                        asset_id = LLLocalGLTFMaterialMgr::getInstance()->getWorldID(tracking_id);
                     }
                     else
                     {
-                        asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id);
+                        asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
                     }
                 }
                 else
@@ -902,13 +903,13 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
             break;
     }
 
-    mOnFloaterCommitCallback(op, mode, asset_id, inventory_id);
+    mOnFloaterCommitCallback(op, mode, asset_id, inventory_id, tracking_id);
 }
 void LLFloaterTexturePicker::commitCancel()
 {
 	if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply)
 	{
-		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null);
+		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null, LLUUID::null);
 	}
 }
 
@@ -962,7 +963,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata)
 	self->setImageID( self->mOriginalImageAssetID );
 	if (self->mOnFloaterCommitCallback)
 	{
-		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null);
+		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null, LLUUID::null);
 	}
 	self->mViewModel->resetDirty();
 	self->closeFloater();
@@ -1167,7 +1168,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
 		{
 			if (self->mOnFloaterCommitCallback)
 			{
-				self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null);
+				self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null, tracking_id);
 			}
 		}
 	}
@@ -1801,7 +1802,7 @@ void LLTextureCtrl::showPicker(BOOL take_focus)
 		}
 		if (texture_floaterp)
 		{
-			texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2, _3, _4));
+			texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2, _3, _4, _5));
 		}
 		if (texture_floaterp)
 		{
@@ -1925,7 +1926,7 @@ void LLTextureCtrl::onFloaterClose()
 	mFloaterHandle.markDead();
 }
 
-void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inv_id)
+void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inv_id, const LLUUID& tracking_id)
 {
     LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
 
@@ -1948,16 +1949,23 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLPickerSource source, co
                 case PICKER_INVENTORY:
                     mImageItemID = inv_id;
                     mImageAssetID = asset_id;
+                    mLocalTrackingID.setNull();
                     break;
                 case PICKER_BAKE:
+                    mImageItemID = LLUUID::null;
+                    mImageAssetID = asset_id;
+                    mLocalTrackingID.setNull();
+                    break;
                 case PICKER_LOCAL:
                     mImageItemID = LLUUID::null;
                     mImageAssetID = asset_id;
+                    mLocalTrackingID = tracking_id;
                     break;
                 case PICKER_UNKNOWN:
                 default:
                     mImageItemID = floaterp->findItemID(asset_id, FALSE);
                     mImageAssetID = asset_id;
+                    mLocalTrackingID.setNull();
                     break;
             }
 
@@ -2015,6 +2023,7 @@ void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id )
 	{
 		mImageItemID.setNull();
 		mImageAssetID = asset_id;
+        mLocalTrackingID.setNull();
 		LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
 		if( floaterp && getEnabled() )
 		{
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index 473092251e60d6f1b82eedf1e510779121bb3616..a4d3518ef5024c50fb3eb370e23d289e34b247d1 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -200,7 +200,11 @@ class LLTextureCtrl
 	void			closeDependentFloater();
 
 	void			onFloaterClose();
-	void			onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& local_id, const LLUUID& inv_id);
+    void			onFloaterCommit(ETexturePickOp op,
+                                    LLPickerSource source,
+                                    const LLUUID& local_id,
+                                    const LLUUID& inv_id,
+                                    const LLUUID& tracking_id);
 
 	// This call is returned when a drag is detected. Your callback
 	// should return TRUE if the drag is acceptable.
@@ -230,6 +234,9 @@ class LLTextureCtrl
     void setInventoryPickType(EPickInventoryType type);
     EPickInventoryType getInventoryPickType() { return mInventoryPickType; };
 
+    bool isImageLocal() { return mLocalTrackingID.notNull(); }
+    LLUUID getLocalTrackingID() { return mLocalTrackingID; }
+
 private:
 	BOOL allowDrop(LLInventoryItem* item, EDragAndDropType cargo_type, std::string& tooltip_msg);
 	BOOL doDrop(LLInventoryItem* item);
@@ -248,6 +255,7 @@ class LLTextureCtrl
 	LLUUID					 	mDefaultImageAssetID;
 	LLUUID					 	mBlankImageAssetID;
 	LLUUID					 	mTransparentImageAssetID;
+    LLUUID						mLocalTrackingID;
 	LLUIImagePtr				mFallbackImage;
 	std::string					mDefaultImageName;
 	LLHandle<LLFloater>			mFloaterHandle;
@@ -273,7 +281,7 @@ class LLTextureCtrl
 #if 0
 //////////////////////////////////////////////////////////////////////////////////////////
 // LLFloaterTexturePicker
-typedef boost::function<void(LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inventory_id)> floater_commit_callback;
+typedef boost::function<void(LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inventory_id, const LLUUID& tracking_id)> floater_commit_callback;
 typedef boost::function<void()> floater_close_callback;
 typedef boost::function<void(const LLUUID& asset_id)> set_image_asset_id_callback;
 typedef boost::function<void(LLPointer<LLViewerTexture> texture)> set_on_update_image_stats_callback;
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 967970cfe8836c8b88a016d579cec3d202a6ae2e..89eada58a7b2537dd8482eab85beeb3c67d33bd3 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -5544,6 +5544,11 @@ S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_ma
             tep->setGLTFRenderMaterial(render_mat);
             retval = TEM_CHANGE_TEXTURE;
 
+            for (LLGLTFMaterial::local_tex_map_t::value_type &val : override_mat->mTrackingIdToLocalTexture)
+            {
+                LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.first, override_mat);
+            }
+
         }
         else if (tep->setGLTFRenderMaterial(nullptr))
         {