diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 8086bcf40270aa74d789f57b66d0fa6e27ed752a..06705a277b48b3e0c74f2344d6e9146d7615b243 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -302,10 +302,6 @@ void LLMaterialEditor::onClickCloseBtn(bool app_quitting)
 
 void LLMaterialEditor::onClose(bool app_quitting)
 {
-    // todo: will only revert whatever was recently selected,
-    // Later should work based of tools floater
-    LLSelectMgr::getInstance()->selectionRevertGLTFMaterials();
-    
     LLPreview::onClose(app_quitting);
 }
 
@@ -1265,6 +1261,73 @@ void LLMaterialEditor::onCancelMsgCallback(const LLSD& notification, const LLSD&
     S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
     if (0 == option)
     {
+        if (mIsOverride && !mObjectOverridesSavedValues.empty())
+        {
+            // Reapply ids back onto selection.
+            // TODO: monitor selection changes and resave on selection changes
+            struct g : public LLSelectedObjectFunctor
+            {
+                g(LLMaterialEditor* me) : mEditor(me) {}
+                virtual bool apply(LLViewerObject* objectp)
+                {
+                    if (!objectp || !objectp->permModify())
+                    {
+                        return false;
+                    }
+
+                    U32 local_id = objectp->getLocalID();
+                    if (mEditor->mObjectOverridesSavedValues.find(local_id) == mEditor->mObjectOverridesSavedValues.end())
+                    {
+                        return false;
+                    }
+
+                    S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
+                    for (U8 te = 0; te < num_tes; te++)
+                    {
+                        if (mEditor->mObjectOverridesSavedValues[local_id].size() > te
+                            && objectp->getTE(te)->isSelected())
+                        {
+                            objectp->setRenderMaterialID(
+                                te,
+                                mEditor->mObjectOverridesSavedValues[local_id][te],
+                                false /*wait for bulk update*/);
+                        }
+                    }
+                    return true;
+                }
+                LLMaterialEditor* mEditor;
+            } restorefunc(this);
+            LLSelectMgr::getInstance()->getSelection()->applyToObjects(&restorefunc);
+
+            struct f : public LLSelectedObjectFunctor
+            {
+                virtual bool apply(LLViewerObject* object)
+                {
+                    if (object && !object->permModify())
+                    {
+                        return false;
+                    }
+
+                    LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)object->getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL);
+                    if (param_block)
+                    {
+                        if (param_block->isEmpty())
+                        {
+                            object->setHasRenderMaterialParams(false);
+                        }
+                        else
+                        {
+                            object->parameterChanged(LLNetworkData::PARAMS_RENDER_MATERIAL, true);
+                        }
+                    }
+
+                    object->sendTEUpdate();
+                    return true;
+                }
+            } sendfunc;
+            LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
+        }
+
         closeFloater();
     }
 }
@@ -1410,6 +1473,32 @@ void LLMaterialEditor::loadLive()
         me->setTitle(me->getString("material_override_title"));
         me->childSetVisible("save", false);
         me->childSetVisible("save_as", false);
+        me->mObjectOverridesSavedValues.clear();
+
+        // Collect ids to be able to revert overrides.
+        // TODO: monitor selection changes and resave on selection changes
+        struct g : public LLSelectedObjectFunctor
+        {
+            g(LLMaterialEditor* me) : mEditor(me) {}
+            virtual bool apply(LLViewerObject* objectp)
+            {
+                if (!objectp)
+                {
+                    return false;
+                }
+
+                U32 local_id = objectp->getLocalID();
+                S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
+                for (U8 te = 0; te < num_tes; te++)
+                {
+                    LLUUID mat_id = objectp->getRenderMaterialID(te);
+                    mEditor->mObjectOverridesSavedValues[local_id].push_back(mat_id);
+                }
+                return true;
+            }
+            LLMaterialEditor* mEditor;
+        } savefunc(me);
+        LLSelectMgr::getInstance()->getSelection()->applyToObjects(&savefunc);
 
         me->openFloater();
         me->setFocus(TRUE);
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index 23cb32aacf65850e2c0dcd7f19a524f3084caa43..60907b18ba0836b63051afd1b1c99f2818da8575 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -276,5 +276,8 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
 
     // if true, this instance is live instance editing overrides
     bool mIsOverride = false;
+    // local id, texture ids per face for object overrides
+    // for "cancel" support
+    std::map<U32, uuid_vec_t> mObjectOverridesSavedValues;
 };
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index aea4353bc8909b157b08c10512e7460902bc8427..8415d457df214731292f9ec04e341643c44e5dd6 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1995,7 +1995,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 	
 	if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
 	{
-		updateRiggedVolume(false);
+        updateRiggedVolume(false);
 		genBBoxes(FALSE);
 		mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
 	}
@@ -4774,7 +4774,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
 	{
 		if ((pick_rigged) || (getAvatar() && (getAvatar()->isSelf()) && (LLFloater::isVisible(gFloaterTools))))
 		{
-			updateRiggedVolume(true, LLRiggedVolume::DO_NOT_UPDATE_FACES);
+            updateRiggedVolume(true, LLRiggedVolume::DO_NOT_UPDATE_FACES);
 			volume = mRiggedVolume;
 			transform = false;
 		}
@@ -4981,7 +4981,7 @@ void LLVOVolume::updateRiggedVolume(bool force_treat_as_rigged, LLRiggedVolume::
 	//Update mRiggedVolume to match current animation frame of avatar. 
 	//Also update position/size in octree.  
 
-	if ((!force_treat_as_rigged) && (!treatAsRigged()))
+    if ((!force_treat_as_rigged) && (!treatAsRigged()))
 	{
 		clearRiggedVolume();
 		
@@ -5010,7 +5010,7 @@ void LLVOVolume::updateRiggedVolume(bool force_treat_as_rigged, LLRiggedVolume::
 		updateRelativeXform();
 	}
 
-	mRiggedVolume->update(skin, avatar, volume, face_index, rebuild_face_octrees);
+    mRiggedVolume->update(skin, avatar, volume, face_index, rebuild_face_octrees);
 }
 
 void LLRiggedVolume::update(
@@ -5173,12 +5173,7 @@ void LLRiggedVolume::update(
             if (rebuild_face_octrees)
 			{
                 dst_face.destroyOctree();
-
-				LLVector4a size;
-				size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]);
-				size.splat(size.getLength3().getF32()*0.5f);
-			
-				dst_face.createOctree(1.f);
+                dst_face.createOctree();
 			}
 		}
 	}
@@ -5428,7 +5423,6 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 	
 	LLViewerTexture* tex = facep->getTexture();
 
-    
 	U8 index = facep->getTextureIndex();
 
     LLMaterial* mat = nullptr;
@@ -5711,7 +5705,7 @@ static inline void add_face(T*** list, U32* count, T* face)
     {
         if (count[1] < MAX_FACE_COUNT)
         {
-            face->setDrawOrderIndex(count[1]);
+            //face->setDrawOrderIndex(count[1]);
             list[1][count[1]++] = face;
         }
     }
@@ -5719,12 +5713,36 @@ static inline void add_face(T*** list, U32* count, T* face)
     {
         if (count[0] < MAX_FACE_COUNT)
         {
-            face->setDrawOrderIndex(count[0]);
+            //face->setDrawOrderIndex(count[0]);
             list[0][count[0]++] = face;
         }
     }
 }
 
+// return index into linkset for given object (0 for root prim)
+U32 get_linkset_index(LLVOVolume* vobj)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE;
+    if (vobj->isRootEdit())
+    {
+        return 0;
+    }
+
+    LLViewerObject* root = vobj->getRootEdit();
+    U32 idx = 1;
+    for (const auto& child : root->getChildren())
+    {
+        if (child == vobj)
+        {
+            return idx;
+        }
+        ++idx;
+    }
+
+    llassert(false);
+    return idx; //should never get here
+}
+
 void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
@@ -5891,6 +5909,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                 avatar->addAttachmentOverridesForObject(vobj, NULL, false);
             }
             
+            U32 linkset_index = get_linkset_index(vobj);
+
             // Standard rigged mesh attachments: 
 			bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment();
             // Animated objects. Have to check for isRiggedMesh() to
@@ -5910,6 +5930,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 					continue;
 				}
 
+                // order by linkset index first and face index second
+                facep->setDrawOrderIndex(linkset_index * 100 + i);
+                
                 // HACK -- brute force this check every time a drawable gets rebuilt
                 vobj->updateTEMaterialTextures(i);
 #if 0
@@ -5949,11 +5972,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                     if (facep->isState(LLFace::RIGGED))
                     { 
                         //face is not rigged but used to be, remove from rigged face pool
-                        LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
-                        if (pool)
-                        {
-                            pool->removeFace(facep);
-                        }
                         facep->clearState(LLFace::RIGGED);
                         facep->mAvatar = NULL;
                         facep->mSkinInfo = NULL;
@@ -6261,7 +6279,7 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 			{
 				LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable();
 
-				if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) && !drawablep->isState(LLDrawable::RIGGED) )
+				if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL))
 				{
 					LLVOVolume* vobj = drawablep->getVOVolume();
 					
@@ -6295,8 +6313,6 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 							LLVertexBuffer* buff = face->getVertexBuffer();
 							if (buff)
 							{
-								llassert(!face->isState(LLFace::RIGGED));
-
 								if (!face->getGeometryVolume(*volume, face->getTEOffset(), 
 									vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex()))
 								{ //something's gone wrong with the vertex buffer accounting, rebuild this group 
@@ -6425,6 +6441,13 @@ struct CompareBatchBreakerRigged
     }
 };
 
+struct CompareDrawOrder
+{
+    bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+    {
+        return lhs->getDrawOrderIndex() < rhs->getDrawOrderIndex();
+    }
+};
 
 U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL rigged)
 {
@@ -6459,6 +6482,11 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
                 //sort faces by things that break batches, including avatar and mesh id
                 std::sort(faces, faces + face_count, CompareBatchBreakerRigged());
             }
+            else
+            {
+                // preserve legacy draw order for rigged faces
+                std::sort(faces, faces + face_count, CompareDrawOrder());
+            }
         }
         else if (!distance_sort)
         {