From d23b6e442e0a4d3a8badb51efd7611e23d138b26 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Wed, 15 Mar 2017 12:08:30 +0200
Subject: [PATCH] MAINT-7168 Avatar rendering settings adjustments

---
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/app_settings/settings.xml       |  11 +
 .../newview/llfloateravatarrendersettings.cpp | 235 ++++++++++++++++++
 indra/newview/llfloateravatarrendersettings.h |  67 +++++
 indra/newview/llfloaterpreference.cpp         |  10 +-
 indra/newview/llfloaterpreference.h           |   1 +
 indra/newview/llmutelist.cpp                  |  96 +++++++
 indra/newview/llmutelist.h                    |  20 ++
 indra/newview/llstartup.cpp                   |   2 +
 indra/newview/llviewerfloaterreg.cpp          |   2 +
 indra/newview/llvoavatar.cpp                  |  21 +-
 indra/newview/llvoavatar.h                    |   2 -
 .../xui/en/floater_avatar_render_settings.xml |  55 ++++
 .../default/xui/en/menu_attachment_other.xml  |  25 +-
 .../default/xui/en/menu_avatar_other.xml      |  23 +-
 .../xui/en/menu_avatar_rendering_settings.xml |  26 ++
 .../xui/en/panel_preferences_graphics1.xml    |  62 +++--
 17 files changed, 602 insertions(+), 58 deletions(-)
 create mode 100644 indra/newview/llfloateravatarrendersettings.cpp
 create mode 100644 indra/newview/llfloateravatarrendersettings.h
 create mode 100644 indra/newview/skins/default/xui/en/floater_avatar_render_settings.xml
 create mode 100644 indra/newview/skins/default/xui/en/menu_avatar_rendering_settings.xml

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index dbec4b5d651..7ed18801cf4 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -203,6 +203,7 @@ set(viewer_SOURCE_FILES
     llfloaterautoreplacesettings.cpp
     llfloateravatar.cpp
     llfloateravatarpicker.cpp
+    llfloateravatarrendersettings.cpp
     llfloateravatartextures.cpp
     llfloaterbeacons.cpp
     llfloaterbigpreview.cpp
@@ -820,6 +821,7 @@ set(viewer_HEADER_FILES
     llfloaterautoreplacesettings.h
     llfloateravatar.h
     llfloateravatarpicker.h
+    llfloateravatarrendersettings.h
     llfloateravatartextures.h
     llfloaterbeacons.h
     llfloaterbigpreview.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index fb644d3d5ad..097a9ac7b9e 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -8426,6 +8426,17 @@
         <key>Value</key>
             <integer>1</integer>
         </map>
+  <key>AlwaysRenderFriends</key>
+    <map>
+      <key>Comment</key>
+      <string>Always render friends regardless of max complexity</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
   <key>RenderAvatar</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llfloateravatarrendersettings.cpp b/indra/newview/llfloateravatarrendersettings.cpp
new file mode 100644
index 00000000000..530b3bd4817
--- /dev/null
+++ b/indra/newview/llfloateravatarrendersettings.cpp
@@ -0,0 +1,235 @@
+/**
+ * @file llfloateravatarrendersettings.cpp
+ * @brief Shows the list of avatars with non-default rendering settings
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateravatarrendersettings.h"
+
+#include "llavatarnamecache.h"
+#include "llfiltereditor.h"
+#include "llfloaterreg.h"
+#include "llnamelistctrl.h"
+#include "llmenugl.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+
+class LLSettingsContextMenu : public LLListContextMenu
+
+{
+public:
+    LLSettingsContextMenu(LLFloaterAvatarRenderSettings* floater_settings)
+        :   mFloaterSettings(floater_settings)
+    {}
+protected:
+    LLContextMenu* createMenu()
+    {
+        LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+        LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+        registrar.add("Settings.SetRendering", boost::bind(&LLFloaterAvatarRenderSettings::onCustomAction, mFloaterSettings, _2, mUUIDs.front()));
+        enable_registrar.add("Settings.IsSelected", boost::bind(&LLFloaterAvatarRenderSettings::isActionChecked, mFloaterSettings, _2, mUUIDs.front()));
+        LLContextMenu* menu = createFromFile("menu_avatar_rendering_settings.xml");
+
+        return menu;
+    }
+
+    LLFloaterAvatarRenderSettings* mFloaterSettings;
+};
+
+class LLAvatarRenderMuteListObserver : public LLMuteListObserver
+{
+    /* virtual */ void onChange()  { LLFloaterAvatarRenderSettings::setNeedsUpdate();}
+};
+
+static LLAvatarRenderMuteListObserver sAvatarRenderMuteListObserver;
+
+LLFloaterAvatarRenderSettings::LLFloaterAvatarRenderSettings(const LLSD& key)
+:   LLFloater(key),
+	mAvatarSettingsList(NULL),
+	mNeedsUpdate(false)
+{
+    mContextMenu = new LLSettingsContextMenu(this);
+    LLRenderMuteList::getInstance()->addObserver(&sAvatarRenderMuteListObserver);
+}
+
+LLFloaterAvatarRenderSettings::~LLFloaterAvatarRenderSettings()
+{
+    delete mContextMenu;
+    LLRenderMuteList::getInstance()->removeObserver(&sAvatarRenderMuteListObserver);
+}
+
+BOOL LLFloaterAvatarRenderSettings::postBuild()
+{
+    LLFloater::postBuild();
+    mAvatarSettingsList = getChild<LLNameListCtrl>("render_settings_list");
+    mAvatarSettingsList->setRightMouseDownCallback(boost::bind(&LLFloaterAvatarRenderSettings::onAvatarListRightClick, this, _1, _2, _3));
+
+    getChild<LLFilterEditor>("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterAvatarRenderSettings::onFilterEdit, this, _2));
+
+	return TRUE;
+}
+
+void LLFloaterAvatarRenderSettings::draw()
+{
+    if(mNeedsUpdate)
+    {
+        updateList();
+        mNeedsUpdate = false;
+    }
+
+    LLFloater::draw();
+}
+
+void LLFloaterAvatarRenderSettings::onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y)
+{
+    LLNameListCtrl* list = dynamic_cast<LLNameListCtrl*>(ctrl);
+    if (!list) return;
+    list->selectItemAt(x, y, MASK_NONE);
+    uuid_vec_t selected_uuids;
+
+    if(list->getCurrentID().notNull())
+    {
+        selected_uuids.push_back(list->getCurrentID());
+        mContextMenu->show(ctrl, selected_uuids, x, y);
+    }
+}
+
+void LLFloaterAvatarRenderSettings::onOpen(const LLSD& key)
+{
+    updateList();
+}
+
+void LLFloaterAvatarRenderSettings::updateList()
+{
+    mAvatarSettingsList->deleteAllItems();
+    LLAvatarName av_name;
+    LLNameListCtrl::NameItem item_params;
+    for (std::map<LLUUID, S32>::iterator iter = LLRenderMuteList::getInstance()->sVisuallyMuteSettingsMap.begin(); iter != LLRenderMuteList::getInstance()->sVisuallyMuteSettingsMap.end(); iter++)
+    {
+        item_params.value = iter->first;
+        LLAvatarNameCache::get(iter->first, &av_name);
+        if(!isHiddenRow(av_name.getCompleteName()))
+        {
+            item_params.columns.add().value(av_name.getCompleteName()).column("name");
+            std::string setting = getString(iter->second == 1 ? "av_never_render" : "av_always_render");
+            item_params.columns.add().value(setting).column("setting");
+            mAvatarSettingsList->addNameItemRow(item_params);
+        }
+    }
+}
+
+void LLFloaterAvatarRenderSettings::onFilterEdit(const std::string& search_string)
+{
+    std::string filter_upper = search_string;
+    LLStringUtil::toUpper(filter_upper);
+    if (mNameFilter != filter_upper)
+    {
+        mNameFilter = filter_upper;
+        mNeedsUpdate = true;
+    }
+}
+
+bool LLFloaterAvatarRenderSettings::isHiddenRow(const std::string& av_name)
+{
+    if (mNameFilter.empty()) return false;
+    std::string upper_name = av_name;
+    LLStringUtil::toUpper(upper_name);
+    return std::string::npos == upper_name.find(mNameFilter);
+}
+
+static LLVOAvatar* find_avatar(const LLUUID& id)
+{
+    LLViewerObject *obj = gObjectList.findObject(id);
+    while (obj && obj->isAttachment())
+    {
+        obj = (LLViewerObject *)obj->getParent();
+    }
+
+    if (obj && obj->isAvatar())
+    {
+        return (LLVOAvatar*)obj;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+
+void LLFloaterAvatarRenderSettings::onCustomAction (const LLSD& userdata, const LLUUID& av_id)
+{
+    const std::string command_name = userdata.asString();
+
+    S32 new_setting = 0;
+    if ("default" == command_name)
+    {
+        new_setting = S32(LLVOAvatar::AV_RENDER_NORMALLY);
+	}
+    else if ("never" == command_name)
+	{
+        new_setting = S32(LLVOAvatar::AV_DO_NOT_RENDER);
+    }
+    else if ("always" == command_name)
+	{
+        new_setting = S32(LLVOAvatar::AV_ALWAYS_RENDER);
+	}
+
+    LLVOAvatar *avatarp = find_avatar(av_id);
+    if (avatarp)
+    {
+        avatarp->setVisualMuteSettings(LLVOAvatar::VisualMuteSettings(new_setting));
+    }
+    else
+    {
+        LLRenderMuteList::getInstance()->saveVisualMuteSetting(av_id, new_setting);
+    }
+}
+
+
+bool LLFloaterAvatarRenderSettings::isActionChecked(const LLSD& userdata, const LLUUID& av_id)
+{
+    const std::string command_name = userdata.asString();
+
+    S32 visual_setting = LLRenderMuteList::getInstance()->getSavedVisualMuteSetting(av_id);
+    if ("default" == command_name)
+    {
+        return (visual_setting == S32(LLVOAvatar::AV_RENDER_NORMALLY));
+    }
+    else if ("never" == command_name)
+    {
+        return (visual_setting == S32(LLVOAvatar::AV_DO_NOT_RENDER));
+    }
+    else if ("always" == command_name)
+    {
+        return (visual_setting == S32(LLVOAvatar::AV_ALWAYS_RENDER));
+    }
+    return false;
+}
+
+void LLFloaterAvatarRenderSettings::setNeedsUpdate()
+{
+    LLFloaterAvatarRenderSettings* instance = LLFloaterReg::getTypedInstance<LLFloaterAvatarRenderSettings>("avatar_render_settings");
+    if(!instance) return;
+    instance->mNeedsUpdate = true;
+}
diff --git a/indra/newview/llfloateravatarrendersettings.h b/indra/newview/llfloateravatarrendersettings.h
new file mode 100644
index 00000000000..367b0620acd
--- /dev/null
+++ b/indra/newview/llfloateravatarrendersettings.h
@@ -0,0 +1,67 @@
+/**
+ * @file llfloateravatarrendersettings.h
+ * @brief Shows the list of avatars with non-default rendering settings
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERAVATARRENDERSETTINGS_H
+#define LL_LLFLOATERAVATARRENDERSETTINGS_H
+
+#include "llfloater.h"
+#include "lllistcontextmenu.h"
+#include "llmutelist.h"
+
+class LLNameListCtrl;
+
+class LLFloaterAvatarRenderSettings : public LLFloater
+{
+public:
+
+    LLFloaterAvatarRenderSettings(const LLSD& key);
+    virtual ~LLFloaterAvatarRenderSettings();
+
+    /*virtual*/ BOOL postBuild();
+    /*virtual*/ void onOpen(const LLSD& key);
+    /*virtual*/ void draw();
+
+    void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y);
+
+    void updateList();
+    void onFilterEdit(const std::string& search_string);
+    void onCustomAction (const LLSD& userdata, const LLUUID& av_id);
+    bool isActionChecked(const LLSD& userdata, const LLUUID& av_id);
+
+    static void setNeedsUpdate();
+
+private:
+    bool isHiddenRow(const std::string& av_name);
+
+    bool mNeedsUpdate;
+    LLListContextMenu* mContextMenu;
+    LLNameListCtrl* mAvatarSettingsList;
+
+    std::string mNameFilter;
+};
+
+
+#endif //LL_LLFLOATERAVATARRENDERSETTINGS_H
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index df4bc043e5c..1f460c05ec2 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -360,6 +360,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
 	mCommitCallbackRegistrar.add("Pref.ClickEnablePopup",		boost::bind(&LLFloaterPreference::onClickEnablePopup, this));
 	mCommitCallbackRegistrar.add("Pref.ClickDisablePopup",		boost::bind(&LLFloaterPreference::onClickDisablePopup, this));	
 	mCommitCallbackRegistrar.add("Pref.LogPath",				boost::bind(&LLFloaterPreference::onClickLogPath, this));
+	mCommitCallbackRegistrar.add("Pref.RenderExceptions",       boost::bind(&LLFloaterPreference::onClickRenderExceptions, this));
 	mCommitCallbackRegistrar.add("Pref.HardwareDefaults",		boost::bind(&LLFloaterPreference::setHardwareDefaults, this));
 	mCommitCallbackRegistrar.add("Pref.AvatarImpostorsEnable",	boost::bind(&LLFloaterPreference::onAvatarImpostorsEnable, this));
 	mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxComplexity",	boost::bind(&LLFloaterPreference::updateMaxComplexity, this));
@@ -385,7 +386,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
 	gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));	
 	gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));	
 	gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged,  _2));
-	
+
 	gSavedSettings.getControl("AppearanceCameraMovement")->getCommitSignal()->connect(boost::bind(&handleAppearanceCameraMovementChanged,  _2));
 
 	LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this );
@@ -786,10 +787,12 @@ void LLFloaterPreference::onOpen(const LLSD& key)
 	LLButton* load_btn = findChild<LLButton>("PrefLoadButton");
 	LLButton* save_btn = findChild<LLButton>("PrefSaveButton");
 	LLButton* delete_btn = findChild<LLButton>("PrefDeleteButton");
+	LLButton* exceptions_btn = findChild<LLButton>("RenderExceptionsButton");
 
 	load_btn->setEnabled(started);
 	save_btn->setEnabled(started);
 	delete_btn->setEnabled(started);
+	exceptions_btn->setEnabled(started);
 }
 
 void LLFloaterPreference::onVertexShaderEnable()
@@ -2075,6 +2078,11 @@ void LLFloaterPreference::onClickSpellChecker()
     LLFloaterReg::showInstance("prefs_spellchecker");
 }
 
+void LLFloaterPreference::onClickRenderExceptions()
+{
+    LLFloaterReg::showInstance("avatar_render_settings");
+}
+
 void LLFloaterPreference::onClickAdvanced()
 {
 	LLFloaterReg::showInstance("prefs_graphics_advanced");
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index ea199cf034c..2bb2e7e9ff8 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -171,6 +171,7 @@ class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver,
 	void onClickPermsDefault();
 	void onClickAutoReplace();
 	void onClickSpellChecker();
+	void onClickRenderExceptions();
 	void onClickAdvanced();
 	void applyUIColor(LLUICtrl* ctrl, const LLSD& param);
 	void getUIColor(LLUICtrl* ctrl, const LLSD& param);
diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp
index 3af9c2f912f..02b28a2bf8e 100644
--- a/indra/newview/llmutelist.cpp
+++ b/indra/newview/llmutelist.cpp
@@ -812,3 +812,99 @@ void LLMuteList::notifyObserversDetailed(const LLMute& mute)
 		it = mObservers.upper_bound(observer);
 	}
 }
+
+LLRenderMuteList::LLRenderMuteList()
+{}
+
+bool LLRenderMuteList::saveToFile()
+{
+    std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "render_mute_settings.txt");
+    LLFILE* fp = LLFile::fopen(filename, "wb");
+    if (!fp)
+    {
+        LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL;
+        return false;
+    }
+    for (std::map<LLUUID, S32>::iterator it = sVisuallyMuteSettingsMap.begin(); it != sVisuallyMuteSettingsMap.end(); ++it)
+    {
+        if (it->second != 0)
+        {
+            std::string id_string;
+            it->first.toString(id_string);
+            fprintf(fp, "%d %s\n", (S32)it->second, id_string.c_str());
+        }
+    }
+    fclose(fp);
+    return true;
+}
+
+bool LLRenderMuteList::loadFromFile()
+{
+	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "render_mute_settings.txt");
+	LLFILE* fp = LLFile::fopen(filename, "rb");
+	if (!fp)
+	{
+		LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL;
+		return false;
+	}
+
+	char id_buffer[MAX_STRING];
+	char buffer[MAX_STRING];
+	while (!feof(fp) && fgets(buffer, MAX_STRING, fp))
+	{
+		id_buffer[0] = '\0';
+		S32 setting = 0;
+		sscanf(buffer, " %d %254s\n", &setting, id_buffer);
+		sVisuallyMuteSettingsMap[LLUUID(id_buffer)] = setting;
+	}
+	fclose(fp);
+    return true;
+}
+
+void LLRenderMuteList::saveVisualMuteSetting(const LLUUID& agent_id, S32 setting)
+{
+    if(setting == 0)
+    {
+        sVisuallyMuteSettingsMap.erase(agent_id);
+    }
+    else
+    {
+        sVisuallyMuteSettingsMap[agent_id] = setting;
+    }
+    saveToFile();
+    notifyObservers();
+}
+
+S32 LLRenderMuteList::getSavedVisualMuteSetting(const LLUUID& agent_id)
+{
+    std::map<LLUUID, S32>::iterator iter = sVisuallyMuteSettingsMap.find(agent_id);
+    if (iter != sVisuallyMuteSettingsMap.end())
+    {
+        return iter->second;
+    }
+
+    return 0;
+}
+
+void LLRenderMuteList::addObserver(LLMuteListObserver* observer)
+{
+    mObservers.insert(observer);
+}
+
+void LLRenderMuteList::removeObserver(LLMuteListObserver* observer)
+{
+    mObservers.erase(observer);
+}
+
+void LLRenderMuteList::notifyObservers()
+{
+    for (observer_set_t::iterator it = mObservers.begin();
+        it != mObservers.end();
+        )
+    {
+        LLMuteListObserver* observer = *it;
+        observer->onChange();
+        // In case onChange() deleted an entry.
+        it = mObservers.upper_bound(observer);
+    }
+}
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
index 4ceddc97fd8..9ab978353bb 100644
--- a/indra/newview/llmutelist.h
+++ b/indra/newview/llmutelist.h
@@ -175,5 +175,25 @@ class LLMuteListObserver
 	virtual void onChangeDetailed(const LLMute& ) { }
 };
 
+class LLRenderMuteList : public LLSingleton<LLRenderMuteList>
+{
+    LLSINGLETON(LLRenderMuteList);
+public:
+	bool loadFromFile();
+	bool saveToFile();
+	S32 getSavedVisualMuteSetting(const LLUUID& agent_id);
+	void saveVisualMuteSetting(const LLUUID& agent_id, S32 setting);
+
+	void addObserver(LLMuteListObserver* observer);
+	void removeObserver(LLMuteListObserver* observer);
+
+	std::map<LLUUID, S32> sVisuallyMuteSettingsMap;
+
+private:
+	void notifyObservers();
+	typedef std::set<LLMuteListObserver*> observer_set_t;
+	observer_set_t mObservers;
+};
+
 
 #endif //LL_MUTELIST_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 347409fcab8..0a85344025b 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -967,6 +967,8 @@ bool idle_startup()
 		// Load media plugin cookies
 		LLViewerMedia::loadCookieFile();
 
+		LLRenderMuteList::getInstance()->loadFromFile();
+
 		//-------------------------------------------------
 		// Handle startup progress screen
 		//-------------------------------------------------
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 46525e84553..cf18299b0b5 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -38,6 +38,7 @@
 #include "llfloaterautoreplacesettings.h"
 #include "llfloateravatar.h"
 #include "llfloateravatarpicker.h"
+#include "llfloateravatarrendersettings.h"
 #include "llfloateravatartextures.h"
 #include "llfloaterbigpreview.h"
 #include "llfloaterbeacons.h"
@@ -194,6 +195,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("auction", "floater_auction.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAuction>);
 	LLFloaterReg::add("avatar", "floater_avatar.xml",  (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatar>);
 	LLFloaterReg::add("avatar_picker", "floater_avatar_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarPicker>);
+	LLFloaterReg::add("avatar_render_settings", "floater_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarRenderSettings>);
 	LLFloaterReg::add("avatar_textures", "floater_avatar_textures.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarTextures>);
 
 	LLFloaterReg::add("beacons", "floater_beacons.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBeacons>);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 760eee17f30..f6a16f7da1a 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -728,7 +728,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	    LLSceneMonitor::getInstance()->freezeAvatar((LLCharacter*)this);
 	}
 
-	mVisuallyMuteSetting = getSavedVisualMuteSettings();
+	mVisuallyMuteSetting = LLVOAvatar::VisualMuteSettings(LLRenderMuteList::getInstance()->getSavedVisualMuteSetting(getID()));
 }
 
 std::string LLVOAvatar::avString() const
@@ -7076,7 +7076,9 @@ BOOL LLVOAvatar::isFullyLoaded() const
 bool LLVOAvatar::isTooComplex() const
 {
 	bool too_complex;
-	if (isSelf() || mVisuallyMuteSetting == AV_ALWAYS_RENDER)
+	bool render_friend =  (LLAvatarTracker::instance().isBuddy(getID()) && gSavedSettings.getBOOL("AlwaysRenderFriends"));
+
+	if (isSelf() || render_friend || mVisuallyMuteSetting == AV_ALWAYS_RENDER)
 	{
 		too_complex = false;
 	}
@@ -9230,25 +9232,12 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
     }
 }
 
-//static
-std::map<LLUUID, LLVOAvatar::VisualMuteSettings> LLVOAvatar::sVisuallyMuteSettingsMap;
-
 void LLVOAvatar::setVisualMuteSettings(VisualMuteSettings set)
 {
     mVisuallyMuteSetting = set;
     mNeedsImpostorUpdate = TRUE;
-    sVisuallyMuteSettingsMap[getID()] = set;
-}
-
-LLVOAvatar::VisualMuteSettings LLVOAvatar::getSavedVisualMuteSettings()
-{
-    std::map<LLUUID, VisualMuteSettings>::iterator iter = sVisuallyMuteSettingsMap.find(getID());
-    if (iter != sVisuallyMuteSettingsMap.end())
-    {
-        return iter->second;
-    }
 
-    return AV_RENDER_NORMALLY;
+    LLRenderMuteList::getInstance()->saveVisualMuteSetting(getID(), S32(set));
 }
 
 void LLVOAvatar::calcMutedAVColor()
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 73e4fbd108b..bd89d4ef23a 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -407,7 +407,6 @@ class LLVOAvatar :
 	};
 	void		setVisualMuteSettings(VisualMuteSettings set);
 	VisualMuteSettings  getVisualMuteSettings()						{ return mVisuallyMuteSetting;	};
-	VisualMuteSettings  getSavedVisualMuteSettings();
 
 	U32 		renderRigid();
 	U32 		renderSkinned();
@@ -441,7 +440,6 @@ class LLVOAvatar :
 
 	VisualMuteSettings		mVisuallyMuteSetting;			// Always or never visually mute this AV
 
-	static std::map<LLUUID, VisualMuteSettings> sVisuallyMuteSettingsMap;
 	//--------------------------------------------------------------------
 	// Morph masks
 	//--------------------------------------------------------------------
diff --git a/indra/newview/skins/default/xui/en/floater_avatar_render_settings.xml b/indra/newview/skins/default/xui/en/floater_avatar_render_settings.xml
new file mode 100644
index 00000000000..dd37e329e58
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_avatar_render_settings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ can_resize="true"
+ positioning="cascading"
+ height="200"
+ min_height="100"
+ min_width="230"
+ layout="topleft"
+ name="floater_avatar_render_settings"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="AVATAR RENDER SETTINGS"
+ width="300">
+    <string
+     name="av_never_render"
+     value="Never"/>
+    <string
+     name="av_always_render"
+     value="Always"/>
+    <filter_editor
+     follows="left|top|right"
+     height="23"
+     layout="topleft"
+     left="8"
+     right="-8"
+     label="Filter People"
+     max_length_chars="300"
+     name="people_filter_input"
+     text_color="Black"
+     text_pad_left="10"
+     top="4" />
+    <name_list
+     allow_select="true"
+     bottom="-8"
+     draw_heading="true"
+     opaque="true"
+     follows="all"
+     left="8"
+     keep_selection_visible_on_reshape="true"
+     item_pad="2"
+     multi_select="false"
+     name="render_settings_list"
+     right="-8"
+     top="32">
+        <name_list.columns
+         label="Name"
+         name="name"
+         relative_width="0.65" />
+        <name_list.columns
+         label="Render setting"
+         name="setting"
+         relative_width="0.35" />
+     </name_list>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_attachment_other.xml b/indra/newview/skins/default/xui/en/menu_attachment_other.xml
index 2f60bab0b7e..49b9ac273d9 100644
--- a/indra/newview/skins/default/xui/en/menu_attachment_other.xml
+++ b/indra/newview/skins/default/xui/en/menu_attachment_other.xml
@@ -130,10 +130,13 @@
    </menu_item_call>
 
    <menu_item_separator />
-    
+      <context_menu
+       label="Render Avatar"
+       layout="topleft"
+        name="Render Avatar">
       <menu_item_check
         name="RenderNormally"
-        label="Render Normally">
+        label="Default">
         <menu_item_check.on_check
           function="Avatar.CheckImpostorMode"
           parameter="0" />
@@ -142,26 +145,26 @@
 	      parameter="0" />
       </menu_item_check>
       <menu_item_check
-        name="DoNotRender"
-        label="Do Not Render">
+        name="AlwaysRenderFully"
+        label="Always">
         <menu_item_check.on_check
           function="Avatar.CheckImpostorMode"
-          parameter="1" />
+          parameter="2" />
 	    <menu_item_check.on_click
 	      function="Avatar.SetImpostorMode"
-	      parameter="1" />
+	      parameter="2" />
       </menu_item_check>
       <menu_item_check
-        name="AlwaysRenderFully"
-        label="Render Fully">
+        name="DoNotRender"
+        label="Never">
         <menu_item_check.on_check
           function="Avatar.CheckImpostorMode"
-          parameter="2" />
+          parameter="1" />
 	    <menu_item_check.on_click
 	      function="Avatar.SetImpostorMode"
-	      parameter="2" />
+	      parameter="1" />
       </menu_item_check>
-
+      </context_menu>
   <menu_item_separator
        layout="topleft" name="Impostor seperator"/>
 
diff --git a/indra/newview/skins/default/xui/en/menu_avatar_other.xml b/indra/newview/skins/default/xui/en/menu_avatar_other.xml
index ddfff234100..c5426cb232d 100644
--- a/indra/newview/skins/default/xui/en/menu_avatar_other.xml
+++ b/indra/newview/skins/default/xui/en/menu_avatar_other.xml
@@ -121,9 +121,13 @@
 
    <menu_item_separator />
     
+ <context_menu
+       label="Render Avatar"
+       layout="topleft"
+        name="Render Avatar">
       <menu_item_check
         name="RenderNormally"
-        label="Render Normally">
+        label="Default">
         <menu_item_check.on_check
           function="Avatar.CheckImpostorMode"
           parameter="0" />
@@ -132,25 +136,26 @@
 	      parameter="0" />
       </menu_item_check>
       <menu_item_check
-        name="DoNotRender"
-        label="Do Not Render">
+        name="AlwaysRenderFully"
+        label="Always">
         <menu_item_check.on_check
           function="Avatar.CheckImpostorMode"
-          parameter="1" />
+          parameter="2" />
 	    <menu_item_check.on_click
 	      function="Avatar.SetImpostorMode"
-	      parameter="1" />
+	      parameter="2" />
       </menu_item_check>
       <menu_item_check
-        name="AlwaysRenderFully"
-        label="Render Fully">
+        name="DoNotRender"
+        label="Never">
         <menu_item_check.on_check
           function="Avatar.CheckImpostorMode"
-          parameter="2" />
+          parameter="1" />
 	    <menu_item_check.on_click
 	      function="Avatar.SetImpostorMode"
-	      parameter="2" />
+	      parameter="1" />
       </menu_item_check>
+      </context_menu>
 
   <menu_item_separator 
     layout="topleft"  name="Impostor seperator"/>
diff --git a/indra/newview/skins/default/xui/en/menu_avatar_rendering_settings.xml b/indra/newview/skins/default/xui/en/menu_avatar_rendering_settings.xml
new file mode 100644
index 00000000000..5163cd3115b
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_avatar_rendering_settings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="Settings">
+    <menu_item_check
+     label="Default"
+     layout="topleft"
+     name="default">
+        <on_click function="Settings.SetRendering" parameter="default"/>
+	<on_check function="Settings.IsSelected" parameter="default" />  
+    </menu_item_check>
+    <menu_item_check
+     label="Always render"
+     layout="topleft"
+     name="always_render">
+        <on_click function="Settings.SetRendering" parameter="always"/>
+	<on_check function="Settings.IsSelected" parameter="always" />  
+    </menu_item_check>
+    <menu_item_check
+     label="Never render"
+     layout="topleft"
+     name="never_render">
+        <on_click function="Settings.SetRendering" parameter="never"/>
+	<on_check function="Settings.IsSelected" parameter="never" />  
+    </menu_item_check>  
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
index 32cbbff8b77..652b7fd0295 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
@@ -231,6 +231,34 @@
      m
   </text>
 
+  <check_box
+    control_name="WindLightUseAtmosShaders"
+    height="16"
+    initial_value="true"
+    label="Atmospheric shaders"
+    layout="topleft"
+    left="30"
+    name="WindLightUseAtmosShaders"
+    top_delta="24"
+    width="280">
+    <check_box.commit_callback
+      function="Pref.VertexShaderEnable" />
+  </check_box>
+
+  <check_box
+    control_name="RenderDeferred"
+    height="16"
+    initial_value="true"
+    label="Advanced Lighting Model"
+    layout="topleft"
+    left="30"
+    name="UseLightShaders"
+    top_delta="24"
+    width="256">
+    <check_box.commit_callback
+      function="Pref.VertexShaderEnable" />
+  </check_box>
+  
   <slider
     control_name="IndirectMaxComplexity"
     tool_tip="Controls at what point a visually complex avatar is drawn as a JellyDoll"
@@ -246,7 +274,7 @@
     max_val="101"
     name="IndirectMaxComplexity"
     show_text="false"
-    top_delta="24"
+    top_delta="60"
     width="300">
     <slider.commit_callback
       function="Pref.UpdateIndirectMaxComplexity"
@@ -265,34 +293,30 @@
     width="65">
        0
   </text>
-
   <check_box
-    control_name="WindLightUseAtmosShaders"
+    control_name="AlwaysRenderFriends"
     height="16"
     initial_value="true"
-    label="Atmospheric shaders"
+    label="Always Render Friends"
     layout="topleft"
     left="30"
-    name="WindLightUseAtmosShaders"
+    name="AlwaysRenderFriends"
     top_delta="24"
-    width="280">
+    width="256">
     <check_box.commit_callback
-      function="Pref.VertexShaderEnable" />
+      function="Pref.RenderFriends" />
   </check_box>
-
-  <check_box
-    control_name="RenderDeferred"
-    height="16"
-    initial_value="true"
-    label="Advanced Lighting Model"
+  <button
+    height="23"
+    label="Exceptions..."
     layout="topleft"
-    left="30"
-    name="UseLightShaders"
+    left="48"
+    name="RenderExceptionsButton"
     top_delta="24"
-    width="256">
-    <check_box.commit_callback
-      function="Pref.VertexShaderEnable" />
-  </check_box>
+    width="100">
+    <button.commit_callback
+      function="Pref.RenderExceptions"/>
+  </button>
 
 <!-- End of Basic Settings block -->
 
-- 
GitLab