diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index d810c6ed251b96a686ffd53f0aece1023b0381a4..7640dd70bd14e07cb7548d48c26d21556ae70b4d 100644
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -512,13 +512,25 @@ S32 LLTextureEntry::setBumpShiny(U8 bump_shiny)
 
 void LLTextureEntry::setGLTFMaterial(LLGLTFMaterial* material)
 { 
-    mGLTFMaterial = material; 
+    // assert on precondtion:
+    // whether or not mGLTFMaterial is null, any existing override should have been nulled out 
+    // before calling setGLTFMaterial
+    // NOTE: if you're hitting this assert, try to make sure calling code is using LLViewerObject::setRenderMaterialID
+    llassert(getGLTFMaterialOverride() == nullptr);
+
+    mGLTFMaterial = material;
     if (mGLTFMaterial == nullptr)
     {
         setGLTFRenderMaterial(nullptr);
     }
 }
 
+void LLTextureEntry::setGLTFMaterialOverride(LLGLTFMaterial* mat)
+{ 
+    llassert(mat == nullptr || getGLTFMaterial() != nullptr); // if override is not null, base material must not be null
+    mGLTFMaterialOverrides = mat; 
+}
+
 LLGLTFMaterial* LLTextureEntry::getGLTFRenderMaterial() const
 { 
     if (mGLTFRenderMaterial.notNull())
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
index e37bc9a3b6016d124c74a76153cd6fea8b0bbbdf..d94e14bd73daac645590f4c37b5eb222a3582efc 100644
--- a/indra/llprimitive/lltextureentry.h
+++ b/indra/llprimitive/lltextureentry.h
@@ -200,7 +200,7 @@ class LLTextureEntry
 
     // GLTF override
     LLGLTFMaterial* getGLTFMaterialOverride() const { return mGLTFMaterialOverrides; }
-    void setGLTFMaterialOverride(LLGLTFMaterial* mat) { mGLTFMaterialOverrides = mat; }
+    void setGLTFMaterialOverride(LLGLTFMaterial* mat);
 
     // GLTF render material
     // nuanced behavior here -- if there is no render material, fall back to getGLTFMaterial, but ONLY for the getter, not the setter
diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp
index cd60d34d2f568c6d981d989092d800b086a31374..3b4e9406bb0d26a707d85c7a426b5b30c71854a3 100644
--- a/indra/newview/llgltfmateriallist.cpp
+++ b/indra/newview/llgltfmateriallist.cpp
@@ -36,13 +36,15 @@
 #include "llviewercontrol.h"
 #include "llviewergenericmessage.h"
 #include "llviewerobjectlist.h"
-
+#include "llcorehttputil.h"
 #include "tinygltf/tiny_gltf.h"
 #include <strstream>
 
 #include "json/reader.h"
 #include "json/value.h"
 
+#include <unordered_set>
+
 LLGLTFMaterialList gGLTFMaterialList;
 
 namespace
@@ -77,27 +79,80 @@ namespace
 
             LLUUID object_id = message["object_id"].asUUID();
             
-            LLViewerObject * obj = gObjectList.findObject(object_id);
-            S32 side = message["side"].asInteger();
-            std::string gltf_json = message["gltf_json"].asString();
-
-            std::string warn_msg, error_msg;
-            LLPointer<LLGLTFMaterial> override_data = new LLGLTFMaterial();
-            bool success = override_data->fromJSON(gltf_json, warn_msg, error_msg);
+            LLViewerObject * obj = gObjectList.findObject(object_id); // NOTE: null object here does NOT mean nothing to do, parse message and queue results for later
+            bool clear_all = true;
 
-            if (!success)
+            if (message.has("sides") && message.has("gltf_json"))
             {
-                LL_WARNS() << "failed to parse GLTF override data.  errors: " << error_msg << " | warnings: " << warn_msg << LL_ENDL;
+                LLSD& sides = message["sides"];
+                LLSD& gltf_json = message["gltf_json"];
+
+                if (sides.isArray() && gltf_json.isArray() &&
+                    sides.size() != 0 &&
+                    sides.size() == gltf_json.size())
+                {
+                    clear_all = false;
+
+                    // message should be interpreted thusly:
+                    ///  sides is a list of face indices
+                    //   gltf_json is a list of corresponding json
+                    //   any side not represented in "sides" has no override
+
+                    // parse json
+                    std::unordered_set<S32> side_set;
+
+                    for (int i = 0; i < sides.size(); ++i)
+                    {
+                        LLPointer<LLGLTFMaterial> override_data = new LLGLTFMaterial();
+                        
+                        std::string gltf_json = message["gltf_json"][i].asString();
+
+                        std::string warn_msg, error_msg;
+                        
+                        bool success = override_data->fromJSON(gltf_json, warn_msg, error_msg);
+
+                        if (!success)
+                        {
+                            LL_WARNS() << "failed to parse GLTF override data.  errors: " << error_msg << " | warnings: " << warn_msg << LL_ENDL;
+                        }
+                        else
+                        {
+                            S32 side = sides[i].asInteger();
+                            // flag this side to not be nulled out later
+                            side_set.insert(sides);
+
+                            if (!obj || !obj->setTEGLTFMaterialOverride(side, override_data))
+                            {
+                                // object not ready to receive override data, queue for later
+                                gGLTFMaterialList.queueOverrideUpdate(object_id, side, override_data);
+                            }
+                        }
+                    }
+
+                    if (obj && side_set.size() != obj->getNumTEs())
+                    { // object exists and at least one texture entry needs to have its override data nulled out
+                        for (int i = 0; i < obj->getNumTEs(); ++i)
+                        {
+                            if (side_set.find(i) == side_set.end())
+                            {
+                                obj->setTEGLTFMaterialOverride(i, nullptr);
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    LL_WARNS() << "Malformed GLTF override message data: " << message << LL_ENDL;
+                }
             }
-            else
-            {
-                if (!obj || !obj->setTEGLTFMaterialOverride(side, override_data))
+            
+            if (clear_all && obj)
+            { // override list was empty or an error occurred, null out all overrides for this object
+                for (int i = 0; i < obj->getNumTEs(); ++i)
                 {
-                     // object not ready to receive override data, queue for later
-                    gGLTFMaterialList.queueOverrideUpdate(object_id, side, override_data);
+                    obj->setTEGLTFMaterialOverride(i, nullptr);
                 }
             }
-
             return true;
         }
     };
@@ -296,3 +351,32 @@ void LLGLTFMaterialList::registerCallbacks()
 {
     gGenericDispatcher.addHandler("GLTFMaterialOverride", &handle_gltf_override_message);
 }
+
+// static
+void LLGLTFMaterialList::modifyMaterialCoro(std::string cap_url, LLSD overrides)
+{
+    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("modifyMaterialCoro", httpPolicy));
+    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+    LLCore::HttpHeaders::ptr_t httpHeaders;
+
+    httpOpts->setFollowRedirects(true);
+
+    LL_DEBUGS() << "Applying override via ModifyMaterialParams cap: " << overrides << LL_ENDL;
+
+    LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, overrides, httpOpts, httpHeaders);
+
+    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+    if (!status)
+    {
+        LL_WARNS() << "Failed to modify material." << LL_ENDL;
+    }
+    else if (!result["success"].asBoolean())
+    {
+        LL_WARNS() << "Failed to modify material: " << result["message"] << LL_ENDL;
+    }
+}
diff --git a/indra/newview/llgltfmateriallist.h b/indra/newview/llgltfmateriallist.h
index ee32dc88253092ecb78070924bb0179e076e93b2..f770f6ecc82ef8af706c87b1c4753e82a8af0632 100644
--- a/indra/newview/llgltfmateriallist.h
+++ b/indra/newview/llgltfmateriallist.h
@@ -49,6 +49,13 @@ class LLGLTFMaterialList
 
     static void registerCallbacks();
 
+    // apply given override data via given cap url
+    //  cap_url -- should be gAgent.getRegionCapability("ModifyMaterialParams")
+    //  overrides -- LLSD map in the format
+    //    "object_id": LLUUID - object to be modified
+    //    "side": integer - index of face to be modified
+    //    "gltf_json" : string - GLTF compliant json of override data (optional, if omitted any existing override data will be cleared)
+    static void modifyMaterialCoro(std::string cap_url, LLSD overrides);
     // save an override update for later (for example, if an override arrived for an unknown object)
     void queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFMaterial* override_data);
 
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 06705a277b48b3e0c74f2344d6e9146d7615b243..5ae16db1f608038775214e21238131972b6f5b71 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -1994,7 +1994,7 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedTEFunctor
                 "side", te,
                 "gltf_json", overrides_json
             );
-            LLCoros::instance().launch("modifyMaterialCoro", std::bind(&LLMaterialEditor::modifyMaterialCoro, mEditor, mCapUrl, overrides));
+            LLCoros::instance().launch("modifyMaterialCoro", std::bind(&LLGLTFMaterialList::modifyMaterialCoro, mCapUrl, overrides));
         }
         return true;
     }
@@ -2452,30 +2452,3 @@ void LLMaterialEditor::loadDefaults()
     setFromGltfModel(model_in, 0, true);
 }
 
-void LLMaterialEditor::modifyMaterialCoro(std::string cap_url, LLSD overrides)
-{
-    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
-    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
-        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("modifyMaterialCoro", httpPolicy));
-    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
-    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
-    LLCore::HttpHeaders::ptr_t httpHeaders;
-
-    httpOpts->setFollowRedirects(true);
-
-    LL_DEBUGS() << "Applying override via ModifyMaterialParams cap: " << overrides << LL_ENDL;
-
-    LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, overrides, httpOpts, httpHeaders);
-
-    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
-    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
-    if (!status)
-    {
-        LL_WARNS() << "Failed to modify material." << LL_ENDL;
-    }
-    else if (!result["success"].asBoolean())
-    {
-        LL_WARNS() << "Failed to modify material: " << result["message"] << LL_ENDL;
-    }
-}
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index 60907b18ba0836b63051afd1b1c99f2818da8575..53f19d877be0ddef2a6c8064aec537cdd671b3a0 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -219,8 +219,6 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     // initialize the UI from a default GLTF material
     void loadDefaults();
 
-    void modifyMaterialCoro(std::string cap_url, LLSD overrides);
-
 private:
     void setFromGLTFMaterial(LLGLTFMaterial* mat);
     bool setFromSelection();
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 2475900d0ea24518bb74b66d44b2081ff1664084..ac508c4e8a79046e93370e160a04b5835c30fd3e 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -1950,48 +1950,14 @@ void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
 
             if (te != -1)
             {
-                LLTextureEntry* tep = objectp->getTE(te);
-                if (asset_id.notNull())
-                {
-                    tep->setGLTFMaterial(gGLTFMaterialList.getMaterial(asset_id));
-                }
-                else
-                {
-                    tep->setGLTFMaterial(nullptr);
-                }
-
-                objectp->faceMappingChanged();
-                gPipeline.markTextured(objectp->mDrawable);
-
-                LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)objectp->getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL);
-                if (param_block)
-                {
-                    param_block->setMaterial(te, asset_id);
-                }
+                objectp->setRenderMaterialID(te, asset_id);
             }
             else // Shouldn't happen?
             {
                 S32 num_faces = objectp->getNumTEs();
                 for (S32 face = 0; face < num_faces; face++)
                 {
-                    LLTextureEntry* tep = objectp->getTE(face);
-                    if (asset_id.notNull())
-                    {
-                        tep->setGLTFMaterial(gGLTFMaterialList.getMaterial(asset_id));
-                    }
-                    else
-                    {
-                        tep->setGLTFMaterial(nullptr);
-                    }
-
-                    objectp->faceMappingChanged();
-                    gPipeline.markTextured(objectp->mDrawable);
-
-                    LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)objectp->getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL);
-                    if (param_block)
-                    {
-                        param_block->setMaterial(face, asset_id);
-                    }
+                    objectp->setRenderMaterialID(te, asset_id);
                 }
             }
 
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index ac519970b74a241e710b93d84113246d905b6fb1..7dbe0652b11a97d63787e11f8a0aad55a187d00a 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -7115,6 +7115,20 @@ const LLUUID& LLViewerObject::getRenderMaterialID(U8 te) const
 
 void LLViewerObject::setRenderMaterialID(U8 te, const LLUUID& id, bool update_server)
 {
+    if (update_server)
+    {
+        // clear out any existing override data and render material
+        getTE(te)->setGLTFMaterialOverride(nullptr);
+        getTE(te)->setGLTFRenderMaterial(nullptr);
+
+        LLCoros::instance().launch("modifyMaterialCoro", 
+            std::bind(&LLGLTFMaterialList::modifyMaterialCoro, 
+                gAgent.getRegionCapability("ModifyMaterialParams"), 
+                llsd::map(
+                    "object_id", getID(),
+                    "side", te)));
+    }
+
     if (id.notNull())
     {
         getTE(te)->setGLTFMaterial(gGLTFMaterialList.getMaterial(id));