diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index 0bb1f1a0fdd49fdd0403ea91e6ee42a4c4d8a1cf..f08cc180366d6faf3e7b933a19be118bd55d14f5 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -96,7 +96,7 @@ LLAssetDictionary::LLAssetDictionary()
 	addEntry(LLAssetType::AT_WIDGET,            new AssetEntry("WIDGET",            "widget",   "widget",           false,      false,      false));
 	addEntry(LLAssetType::AT_PERSON,            new AssetEntry("PERSON",            "person",   "person",           false,      false,      false));
 	addEntry(LLAssetType::AT_SETTINGS,          new AssetEntry("SETTINGS",          "settings", "settings blob",    true,       true,       true));
-	addEntry(LLAssetType::AT_MATERIAL,     new AssetEntry("MATERIAL",          "material", "render material",  true,       true,       true));
+	addEntry(LLAssetType::AT_MATERIAL,          new AssetEntry("MATERIAL",          "material", "render material",  true,       true,       true));
 	addEntry(LLAssetType::AT_UNKNOWN,           new AssetEntry("UNKNOWN",           "invalid",  NULL,               false,      false,      false));
     addEntry(LLAssetType::AT_NONE,              new AssetEntry("NONE",              "-1",		NULL,		  		FALSE,		FALSE,		FALSE));
 
diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp
index 57d521429c1684cf04b68264912be0d3cbfdad9d..ceda2f3caf3292d57ed496f02521fe56e37bb5b3 100644
--- a/indra/llinventory/llinventorytype.cpp
+++ b/indra/llinventory/llinventorytype.cpp
@@ -86,6 +86,7 @@ LLInventoryDictionary::LLInventoryDictionary()
 	addEntry(LLInventoryType::IT_WIDGET,              new InventoryEntry("widget",    "widget",        1, LLAssetType::AT_WIDGET));
 	addEntry(LLInventoryType::IT_PERSON,              new InventoryEntry("person",    "person",        1, LLAssetType::AT_PERSON));
     addEntry(LLInventoryType::IT_SETTINGS,            new InventoryEntry("settings",  "settings",      1, LLAssetType::AT_SETTINGS));
+	addEntry(LLInventoryType::IT_MATERIAL,            new InventoryEntry("material",  "render material", 1, LLAssetType::AT_MATERIAL));
 }
 
 
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 09d3f9573618ecf6a748dd3fc4023923d6b1a419..896c4d2366efeff13369c3bfa5bf65f5f48da580 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1170,10 +1170,14 @@ void LLShaderMgr::initAttribsAndUniforms()
 	llassert(mReservedUniforms.size() == LLShaderMgr::PROJECTOR_AMBIENT_LOD+1);
 
 	mReservedUniforms.push_back("color");
-		
+    mReservedUniforms.push_back("emissiveColor");
+    mReservedUniforms.push_back("metallicFactor");
+    mReservedUniforms.push_back("roughnessFactor");
+    
 	mReservedUniforms.push_back("diffuseMap");
     mReservedUniforms.push_back("altDiffuseMap");
 	mReservedUniforms.push_back("specularMap");
+    mReservedUniforms.push_back("emissiveMap");
 	mReservedUniforms.push_back("bumpMap");
     mReservedUniforms.push_back("bumpMap2");
 	mReservedUniforms.push_back("environmentMap");
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 067df6fa043cfd71dca73c41a52ff972bed2d80b..3c68aa5e7912a5673760a571fbba308b3bb4900d 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -74,9 +74,13 @@ class LLShaderMgr
         PROJECTOR_LOD,                      //  "proj_lod"
         PROJECTOR_AMBIENT_LOD,              //  "proj_ambient_lod"
         DIFFUSE_COLOR,                      //  "color"
+        EMISSIVE_COLOR,                     //  "emissiveColor"
+        METALLIC_FACTOR,                    //  "metallicFactor"
+        ROUGHNESS_FACTOR,                   //  "roughnessFactor"
         DIFFUSE_MAP,                        //  "diffuseMap"
         ALTERNATE_DIFFUSE_MAP,              //  "altDiffuseMap"
         SPECULAR_MAP,                       //  "specularMap"
+        EMISSIVE_MAP,                       //  "emissiveMap"
         BUMP_MAP,                           //  "bumpMap"
         BUMP_MAP2,                          //  "bumpMap2"
         ENVIRONMENT_MAP,                    //  "environmentMap"
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
index d5b4e278bc574290d817c217daa6eaa97f3c96da..e4be88926f1e2123649a944e3b0e13fd59cd2a39 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
@@ -27,15 +27,24 @@
 
 #define DEBUG_BASIC         0
 #define DEBUG_VERTEX        0
-#define DEBUG_NORMAL        0
+#define DEBUG_NORMAL_RAW    0 // Output packed normal map "as is" to diffuse
+#define DEBUG_NORMAL_OUT    0 // Output unpacked normal to diffuse
 #define DEBUG_POSITION      0
 
 uniform sampler2D diffuseMap;  //always in sRGB space
 
+uniform float metallicFactor;
+uniform float roughnessFactor;
+uniform vec3 emissiveColor;
+
 #ifdef HAS_NORMAL_MAP
     uniform sampler2D bumpMap;
 #endif
 
+#ifdef HAS_EMISSIVE_MAP
+    uniform sampler2D emissiveMap;
+#endif
+
 #ifdef HAS_SPECULAR_MAP
     uniform sampler2D specularMap; // Packed: Occlusion, Metal, Roughness
 #endif
@@ -76,8 +85,6 @@ void main()
 // else
     vec3 col = vertex_color.rgb * texture2D(diffuseMap, vary_texcoord0.xy).rgb;
 
-    vec3 emissive = vec3(0);
-
 #ifdef HAS_NORMAL_MAP
     vec4 norm = texture2D(bumpMap, vary_texcoord1.xy);
     norm.xyz = norm.xyz * 2 - 1;
@@ -105,6 +112,14 @@ void main()
     vec3 spec = vec3(1,1,1);
 #endif
     
+    spec.g *= roughnessFactor;
+    spec.b *= metallicFactor;
+
+    vec3 emissive = emissiveColor;
+#ifdef HAS_EMISSIVE_MAP
+    emissive *= texture2D(emissiveMap, vary_texcoord0.xy).rgb;
+#endif
+
 
 #if DEBUG_BASIC
     col.rgb = vec3( 1, 0, 1 );
@@ -112,15 +127,18 @@ void main()
 #if DEBUG_VERTEX
     col.rgb = vertex_color.rgb;
 #endif
-#if DEBUG_NORMAL
+#if DEBUG_NORMAL_RAW
+    col.rgb = texture2D(bumpMap, vary_texcoord1.xy).rgb;
+#endif
+#if DEBUG_NORMAL_OUT
     col.rgb = vary_normal;
 #endif
 #if DEBUG_POSITION
     col.rgb = vary_position.xyz;
 #endif
 
-    frag_data[0] = vec4(col, 0.0);
-    frag_data[1] = vec4(spec.rgb, vertex_color.a);                                      // Occlusion, Roughness, Metal
-    frag_data[2] = vec4(encode_normal(tnorm), vertex_color.a, GBUFFER_FLAG_HAS_PBR); //
-    frag_data[3] = vec4(emissive,0);                                                    // Emissive
+    frag_data[0] = vec4(col, 0.0);                                                   // Diffuse
+    frag_data[1] = vec4(spec.rgb, vertex_color.a);                                   // Occlusion, Roughness, Metal
+    frag_data[2] = vec4(encode_normal(tnorm), vertex_color.a, GBUFFER_FLAG_HAS_PBR); // normal, environment intensity, flags
+    frag_data[3] = vec4(emissive,0);                                                 // Emissive
 }
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
index f2ae6991309a0d58592bf099b9e724dcf6c6f00d..243f1c44985826dc491b5439106c157b8788c27a 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
@@ -27,23 +27,26 @@
 #define DEBUG_PBR_PACKORM1        0 // Rough=1, Metal=1
 #define DEBUG_PBR_TANGENT1        1 // Tangent = 1,0,0
 
-#define DEBUG_PBR_RAW_DIFF        0 // Output: use diffuse in G-Buffer
-#define DEBUG_PBR_RAW_SPEC        0 // Output: use spec in G-Buffer
-#define DEBUG_PBR_IRRADIANCE      0 // Output: Diffuse Irradiance
-#define DEBUG_PBR_DIFFUSE         0 // Output: Radiance Lambertian
-#define DEBUG_PBR_ORM             0 // Output: Packed Occlusion Roughness Metal
-#define DEBUG_PBR_ROUGH           0 // Output: grayscale roughenss
-#define DEBUG_PBR_METAL           0 // Output: grayscale metal
-#define DEBUG_PBR_REFLECTANCE     0 // Output: diffuse reflectance
-#define DEBUG_PBR_SPEC            0 // Output: Final spec
-#define DEBUG_PBR_SPEC_REFLECTION 0 // Output: reflection
-#define DEBUG_PBR_NORMAL          0 // Output: passed in normal
-#define DEBUG_PBR_VIEW            0 // Output: view_dir
-#define DEBUG_PBR_BRDF            0 // Output: Environment BRDF
-#define DEBUG_PBR_DOT_NV          0 // Output:
-#define DEBUG_PBR_DOT_TV          0 // Output:
-#define DEBUG_PBR_DOT_BV          0 // Output:
-#define DEBUG_PBR_FRESNEL         0 // Output: roughness dependent fresnel
+#define DEBUG_PBR_RAW_DIFF         0 // Output: use diffuse in G-Buffer
+#define DEBUG_PBR_RAW_SPEC         0 // Output: use spec in G-Buffer
+#define DEBUG_PBR_IRRADIANCE       0 // Output: Diffuse Irradiance
+#define DEBUG_PBR_DIFFUSE          0 // Output: Radiance Lambertian
+#define DEBUG_PBR_ORM              0 // Output: Packed Occlusion Roughness Metal
+#define DEBUG_PBR_ROUGH_PERCEPTUAL 0 // Output: grayscale Perceptual Roughenss
+#define DEBUG_PBR_ROUGH_ALPHA      0 // Output: grayscale Alpha Roughness
+#define DEBUG_PBR_METAL            0 // Output: grayscale metal
+#define DEBUG_PBR_REFLECTANCE      0 // Output: diffuse reflectance
+#define DEBUG_PBR_BRDF_UV          0 // Output: red green BRDF UV         (GGX input)
+#define DEBUG_PBR_BRDF_SCALE_BIAS  0 // Output: red green BRDF Scale Bias (GGX output)
+#define DEBUG_PBR_SPEC             0 // Output: Final spec
+#define DEBUG_PBR_SPEC_REFLECTION  0 // Output: reflection
+#define DEBUG_PBR_NORMAL           0 // Output: passed in normal. To see raw normal map: set DEBUG_PBR_RAW_DIFF 1, and in pbropaqueF set DEBUG_NORMAL_RAW
+#define DEBUG_PBR_VIEW             0 // Output: view_dir
+#define DEBUG_PBR_BRDF             0 // Output: Environment BRDF
+#define DEBUG_PBR_DOT_NV           0 // Output: grayscale dot(Normal,ViewDir)
+#define DEBUG_PBR_DOT_TV           0 // Output:
+#define DEBUG_PBR_DOT_BV           0 // Output:
+#define DEBUG_PBR_FRESNEL          0 // Output: roughness dependent fresnel
 
 #extension GL_ARB_texture_rectangle : enable
 #extension GL_ARB_shader_texture_lod : enable
@@ -254,8 +257,11 @@ void main()
 
         color.rgb  = colorDiffuse + colorEmissive + colorSpec;
 
-    #if DEBUG_PBR_BRDF
-        color.rgb = vec3(vScaleBias,0);
+    #if DEBUG_PBR_BRDF_UV
+        color.rgb = vec3(brdfPoint,0.0);
+    #endif
+    #if DEBUG_PBR_BRDF_SCALE_BIAS
+        color.rgb = vec3(vScaleBias,0.0);
     #endif
     #if DEBUG_PBR_FRESNEL
         color.rgb = fresnelR;
@@ -281,9 +287,12 @@ void main()
     #if DEBUG_PBR_METAL
         color.rgb = vec3(metal);
     #endif
-    #if DEBUG_PBR_ROUGH
+    #if DEBUG_PBR_ROUGH_PERCEPTUAL
         color.rgb = vec3(perceptualRough);
     #endif
+    #if DEBUG_PBR_ROUGH_ALPHA
+        color.rgb = vec3(alphaRough);
+    #endif
     #if DEBUG_PBR_SPEC
         color.rgb = colorSpec;
     #endif
diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp
index 86b3ac0d46e3aaaf97aef106134a27d7913e991b..0c257a33a5371ed3bd4b9136bff520d7d6fb50d2 100644
--- a/indra/newview/lldrawpoolpbropaque.cpp
+++ b/indra/newview/lldrawpoolpbropaque.cpp
@@ -99,30 +99,48 @@ void LLDrawPoolPBROpaque::renderDeferred(S32 pass)
     {
         LLDrawInfo& params = **i;
 
-//gGL.getTexUnit(0)->activate();
+        //gGL.getTexUnit(0)->activate();
 
-        if (mShaderLevel > 1)
+        if (params.mTexture.notNull())
         {
-            if (params.mTexture.notNull())
-            {
-                gGL.getTexUnit(0)->bindFast(params.mTexture); // diffuse
-            }
+            gGL.getTexUnit(0)->bindFast(params.mTexture); // diffuse
+        }
+        else
+        {
+            gGL.getTexUnit(0)->bindFast(LLViewerFetchedTexture::sWhiteImagep);
         }
 
         if (params.mNormalMap)
         {
             gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::BUMP_MAP, params.mNormalMap);
         }
+        else
+        {
+            // TODO: bind default normal map (???? WTF is it ???)
+        }
 
         if (params.mSpecularMap)
         {
             gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::SPECULAR_MAP, params.mSpecularMap); // Packed Occlusion Roughness Metal
         }
+        else
+        {
+            gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
+        }
+
+        if (params.mEmissiveMap)
+        {
+            gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::EMISSIVE_MAP, params.mEmissiveMap); // Packed Occlusion Roughness Metal
+        }
+        else
+        {
+            gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::EMISSIVE_MAP, LLViewerFetchedTexture::sWhiteImagep);
+        }
+        
+        gDeferredPBROpaqueProgram.uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, params.mGLTFMaterial->mRoughnessFactor);
+        gDeferredPBROpaqueProgram.uniform1f(LLShaderMgr::METALLIC_FACTOR, params.mGLTFMaterial->mMetallicFactor);
+        gDeferredPBROpaqueProgram.uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, params.mGLTFMaterial->mEmissiveColor.mV);
 
-        // Similar to LLDrawPooLMaterials::pushMaterialsBatch(params, getVertexDataMask(), false);
         LLRenderPass::pushBatch(params, getVertexDataMask(), FALSE, FALSE);
-        //LLRenderPass::applyModelMatrix(params);
-        //params.mVertexBuffer->setBufferFast(getVertexDataMask());
-        //params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
     }
 }
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index d1ea5409edf603bb7790f757568e2596e76dc9e2..071e7708117a5b7aa8d5390e647a0664e7b50839 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -1384,6 +1384,11 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 
 	LLColor4U color = tep->getColor();
 
+    if (tep->getGLTFMaterial())
+    {
+        color = tep->getGLTFMaterial()->mAlbedoColor;
+    }
+
 	if (rebuild_color)
 	{ //decide if shiny goes in alpha channel of color
 		if (tep && 
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index 707ff2b7b6170ed66ec3ff69633bf9676a69beaa..f7dc493109d345db17ea422dcdd9a1a698b681bc 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -1306,6 +1306,18 @@ const std::string& LLInventoryFilter::getFilterText()
 		filtered_by_all_types = FALSE;
 	}
 
+	if (isFilterObjectTypesWith(LLInventoryType::IT_MATERIAL))
+	{
+		filtered_types +=  LLTrans::getString("Materials");
+		filtered_by_type = TRUE;
+		num_filter_types++;
+	}
+	else
+	{
+		not_filtered_types +=  LLTrans::getString("Materials");
+		filtered_by_all_types = FALSE;
+	}
+
 	if (isFilterObjectTypesWith(LLInventoryType::IT_NOTECARD))
 	{
 		filtered_types +=  LLTrans::getString("Notecards");
diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp
index 44e493fdf45cdaace9a0f80c16b11c8e6ace9397..e9b0e8404a2bdeba7cac933e43519543237f628f 100644
--- a/indra/newview/llinventoryicon.cpp
+++ b/indra/newview/llinventoryicon.cpp
@@ -99,6 +99,8 @@ LLIconDictionary::LLIconDictionary()
     addEntry(LLInventoryType::ICONNAME_SETTINGS_DAY,            new IconEntry("Inv_SettingsDay"));
     addEntry(LLInventoryType::ICONNAME_SETTINGS,                new IconEntry("Inv_Settings"));
 
+    addEntry(LLInventoryType::ICONNAME_MATERIAL,                new IconEntry("Inv_Material"));
+
 	addEntry(LLInventoryType::ICONNAME_INVALID, 				new IconEntry("Inv_Invalid"));
 	addEntry(LLInventoryType::ICONNAME_UNKNOWN, 				new IconEntry("Inv_Unknown"));
 
@@ -177,6 +179,9 @@ const std::string& LLInventoryIcon::getIconName(LLAssetType::EType asset_type,
 		case LLAssetType::AT_SETTINGS:
 			idx = assignSettingsIcon(misc_flag);
 			break;
+		case LLAssetType::AT_MATERIAL:
+			idx = LLInventoryType::ICONNAME_MATERIAL;
+			break;
 		case LLAssetType::AT_UNKNOWN:
 			idx = LLInventoryType::ICONNAME_UNKNOWN;
 		default:
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 828b36806316113540e52e3061a4d2885b524e39..1744bf90337473a7ba0ac7ab58995939774af302 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -29,17 +29,20 @@
 #include "llmaterialeditor.h"
 
 #include "llagent.h"
+#include "llappviewer.h"
 #include "llcombobox.h"
 #include "llinventorymodel.h"
+#include "llnotificationsutil.h"
+#include "lltexturectrl.h"
+#include "lltrans.h"
 #include "llviewermenufile.h"
-#include "llappviewer.h"
 #include "llviewertexture.h"
-#include "llnotificationsutil.h"
 #include "llsdutil.h"
 #include "llselectmgr.h"
 #include "llviewerinventory.h"
 #include "llviewerregion.h"
 #include "llvovolume.h"
+#include "llcolorswatch.h"
 
 #include "tinygltf/tiny_gltf.h"
 
@@ -50,23 +53,84 @@
 // Default constructor
 LLMaterialEditor::LLMaterialEditor(const LLSD& key)
     : LLFloater(key)
+    , mHasUnsavedChanges(false)
 {
 }
 
 BOOL LLMaterialEditor::postBuild()
 {
+    mAlbedoTextureCtrl = getChild<LLTextureCtrl>("albedo_texture");
+    mMetallicTextureCtrl = getChild<LLTextureCtrl>("metallic_roughness_texture");
+    mEmissiveTextureCtrl = getChild<LLTextureCtrl>("emissive_texture");
+    mNormalTextureCtrl = getChild<LLTextureCtrl>("normal_texture");
+
+    mAlbedoTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitAlbedoTexture, this, _1, _2));
+    mMetallicTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitMetallicTexture, this, _1, _2));
+    mEmissiveTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitEmissiveTexture, this, _1, _2));
+    mNormalTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitNormalTexture, this, _1, _2));
+
     childSetAction("save", boost::bind(&LLMaterialEditor::onClickSave, this));
+    childSetAction("save_as", boost::bind(&LLMaterialEditor::onClickSaveAs, this));
+    childSetAction("cancel", boost::bind(&LLMaterialEditor::onClickCancel, this));
+
+    boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void*) { setHasUnsavedChanges(true); };
+ 
+    childSetCommitCallback("double sided", changes_callback, NULL);
+
+    // Albedo
+    childSetCommitCallback("albedo color", changes_callback, NULL);
+    getChild<LLColorSwatchCtrl>("albedo color")->setCanApplyImmediately(TRUE);
+    childSetCommitCallback("transparency", changes_callback, NULL);
+    childSetCommitCallback("alpha mode", changes_callback, NULL);
+    childSetCommitCallback("alpha cutoff", changes_callback, NULL);
+
+    // Metallic-Roughness
+    childSetCommitCallback("metalness factor", changes_callback, NULL);
+    childSetCommitCallback("roughness factor", changes_callback, NULL);
+
+    // Metallic-Roughness
+    childSetCommitCallback("metalness factor", changes_callback, NULL);
+    childSetCommitCallback("roughness factor", changes_callback, NULL);
+
+    // Emissive
+    childSetCommitCallback("emissive color", changes_callback, NULL);
+    getChild<LLColorSwatchCtrl>("emissive color")->setCanApplyImmediately(TRUE);
+
+    childSetVisible("unsaved_changes", mHasUnsavedChanges);
+
 	return LLFloater::postBuild();
 }
 
+void LLMaterialEditor::onClickCloseBtn(bool app_quitting)
+{
+    if (app_quitting)
+    {
+        closeFloater(app_quitting);
+    }
+    else
+    {
+        onClickCancel();
+    }
+}
+
 LLUUID LLMaterialEditor::getAlbedoId()
 {
-    return childGetValue("albedo texture").asUUID();
+    return mAlbedoTextureCtrl->getValue().asUUID();
 }
 
 void LLMaterialEditor::setAlbedoId(const LLUUID& id)
 {
-    childSetValue("albedo texture", id);
+    mAlbedoTextureCtrl->setValue(id);
+    mAlbedoTextureCtrl->setDefaultImageAssetID(id);
+
+    if (id.notNull())
+    {
+        // todo: this does not account for posibility of texture
+        // being from inventory, need to check that
+        childSetValue("albedo_upload_fee", getString("upload_fee_string"));
+        // Only set if we will need to upload this texture
+        mAlbedoTextureUploadId = id;
+    }
 }
 
 LLColor4 LLMaterialEditor::getAlbedoColor()
@@ -76,7 +140,6 @@ LLColor4 LLMaterialEditor::getAlbedoColor()
     return ret;
 }
 
-
 void LLMaterialEditor::setAlbedoColor(const LLColor4& color)
 {
     childSetValue("albedo color", color.getValue());
@@ -108,14 +171,29 @@ void LLMaterialEditor::setAlphaCutoff(F32 alpha_cutoff)
     childSetValue("alpha cutoff", alpha_cutoff);
 }
 
+void LLMaterialEditor::setMaterialName(const std::string &name)
+{
+    setTitle(name);
+    mMaterialName = name;
+}
+
 LLUUID LLMaterialEditor::getMetallicRoughnessId()
 {
-    return childGetValue("metallic-roughness texture").asUUID();
+    return mMetallicTextureCtrl->getValue().asUUID();
 }
 
 void LLMaterialEditor::setMetallicRoughnessId(const LLUUID& id)
 {
-    childSetValue("metallic-roughness texture", id);
+    mMetallicTextureCtrl->setValue(id);
+    mMetallicTextureCtrl->setDefaultImageAssetID(id);
+
+    if (id.notNull())
+    {
+        // todo: this does not account for posibility of texture
+        // being from inventory, need to check that
+        childSetValue("metallic_upload_fee", getString("upload_fee_string"));
+        mMetallicTextureUploadId = id;
+    }
 }
 
 F32 LLMaterialEditor::getMetalnessFactor()
@@ -140,12 +218,21 @@ void LLMaterialEditor::setRoughnessFactor(F32 factor)
 
 LLUUID LLMaterialEditor::getEmissiveId()
 {
-    return childGetValue("emissive texture").asUUID();
+    return mEmissiveTextureCtrl->getValue().asUUID();
 }
 
 void LLMaterialEditor::setEmissiveId(const LLUUID& id)
 {
-    childSetValue("emissive texture", id);
+    mEmissiveTextureCtrl->setValue(id);
+    mEmissiveTextureCtrl->setDefaultImageAssetID(id);
+
+    if (id.notNull())
+    {
+        // todo: this does not account for posibility of texture
+        // being from inventory, need to check that
+        childSetValue("emissive_upload_fee", getString("upload_fee_string"));
+        mEmissiveTextureUploadId = id;
+    }
 }
 
 LLColor4 LLMaterialEditor::getEmissiveColor()
@@ -160,12 +247,21 @@ void LLMaterialEditor::setEmissiveColor(const LLColor4& color)
 
 LLUUID LLMaterialEditor::getNormalId()
 {
-    return childGetValue("normal texture").asUUID();
+    return mNormalTextureCtrl->getValue().asUUID();
 }
 
 void LLMaterialEditor::setNormalId(const LLUUID& id)
 {
-    childSetValue("normal texture", id);
+    mNormalTextureCtrl->setValue(id);
+    mNormalTextureCtrl->setDefaultImageAssetID(id);
+
+    if (id.notNull())
+    {
+        // todo: this does not account for posibility of texture
+        // being from inventory, need to check that
+        childSetValue("normal_upload_fee", getString("upload_fee_string"));
+        mNormalTextureUploadId = id;
+    }
 }
 
 bool LLMaterialEditor::getDoubleSided()
@@ -178,6 +274,76 @@ void LLMaterialEditor::setDoubleSided(bool double_sided)
     childSetValue("double sided", double_sided);
 }
 
+void LLMaterialEditor::setHasUnsavedChanges(bool value)
+{
+    if (value != mHasUnsavedChanges)
+    {
+        mHasUnsavedChanges = value;
+        childSetVisible("unsaved_changes", value);
+    }
+
+    // HACK -- apply any changes to selection immediately
+    applyToSelection();
+}
+
+void LLMaterialEditor::onCommitAlbedoTexture(LLUICtrl * ctrl, const LLSD & data)
+{
+    // might be better to use arrays, to have a single callback
+    // and not to repeat the same thing for each tecture control
+    LLUUID new_val = mAlbedoTextureCtrl->getValue().asUUID();
+    if (new_val == mAlbedoTextureUploadId && mAlbedoTextureUploadId.notNull())
+    {
+        childSetValue("albedo_upload_fee", getString("upload_fee_string"));
+    }
+    else
+    {
+        childSetValue("albedo_upload_fee", getString("no_upload_fee_string"));
+    }
+    setHasUnsavedChanges(true);
+}
+
+void LLMaterialEditor::onCommitMetallicTexture(LLUICtrl * ctrl, const LLSD & data)
+{
+    LLUUID new_val = mMetallicTextureCtrl->getValue().asUUID();
+    if (new_val == mMetallicTextureUploadId && mMetallicTextureUploadId.notNull())
+    {
+        childSetValue("metallic_upload_fee", getString("upload_fee_string"));
+    }
+    else
+    {
+        childSetValue("metallic_upload_fee", getString("no_upload_fee_string"));
+    }
+    setHasUnsavedChanges(true);
+}
+
+void LLMaterialEditor::onCommitEmissiveTexture(LLUICtrl * ctrl, const LLSD & data)
+{
+    LLUUID new_val = mEmissiveTextureCtrl->getValue().asUUID();
+    if (new_val == mEmissiveTextureUploadId && mEmissiveTextureUploadId.notNull())
+    {
+        childSetValue("emissive_upload_fee", getString("upload_fee_string"));
+    }
+    else
+    {
+        childSetValue("emissive_upload_fee", getString("no_upload_fee_string"));
+    }
+    setHasUnsavedChanges(true);
+}
+
+void LLMaterialEditor::onCommitNormalTexture(LLUICtrl * ctrl, const LLSD & data)
+{
+    LLUUID new_val = mNormalTextureCtrl->getValue().asUUID();
+    if (new_val == mNormalTextureUploadId && mNormalTextureUploadId.notNull())
+    {
+        childSetValue("normal_upload_fee", getString("upload_fee_string"));
+    }
+    else
+    {
+        childSetValue("normal_upload_fee", getString("no_upload_fee_string"));
+    }
+    setHasUnsavedChanges(true);
+}
+
 
 static void write_color(const LLColor4& color, std::vector<double>& c)
 {
@@ -272,7 +438,7 @@ void LLMaterialEditor::onClickSave()
     
     std::string dump = str.str();
 
-    LL_INFOS() << dump << LL_ENDL;
+    LL_INFOS() << mMaterialName << ": " << dump << LL_ENDL;
 
     // gen a new uuid for this asset
     LLTransactionID tid;
@@ -313,6 +479,54 @@ void LLMaterialEditor::onClickSave()
     );
 }
 
+void LLMaterialEditor::onClickSaveAs()
+{
+    LLSD args;
+    args["DESC"] = mMaterialName;
+
+    LLNotificationsUtil::add("SaveMaterialAs", args, LLSD(), boost::bind(&LLMaterialEditor::onSaveAsMsgCallback, this, _1, _2));
+}
+
+void LLMaterialEditor::onSaveAsMsgCallback(const LLSD& notification, const LLSD& response)
+{
+    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+    if (0 == option)
+    {
+        std::string new_name = response["message"].asString();
+        LLStringUtil::trim(new_name);
+        if (!new_name.empty())
+        {
+            setMaterialName(new_name);
+            onClickSave();
+        }
+        else
+        {
+            LLNotificationsUtil::add("InvalidMaterialName");
+        }
+    }
+}
+
+void LLMaterialEditor::onClickCancel()
+{
+    if (mHasUnsavedChanges)
+    {
+        LLNotificationsUtil::add("UsavedMaterialChanges", LLSD(), LLSD(), boost::bind(&LLMaterialEditor::onCancelMsgCallback, this, _1, _2));
+    }
+    else
+    {
+        closeFloater();
+    }
+}
+
+void LLMaterialEditor::onCancelMsgCallback(const LLSD& notification, const LLSD& response)
+{
+    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+    if (0 == option)
+    {
+        closeFloater();
+    }
+}
+
 class LLMaterialFilePicker : public LLFilePickerThread
 {
 public:
@@ -358,28 +572,115 @@ const tinygltf::Image* get_image_from_texture_index(const tinygltf::Model& model
     return nullptr;
 }
 
-static LLViewerFetchedTexture* get_texture(const std::string& folder, const tinygltf::Model& model, S32 texture_index)
+static LLImageRaw* get_texture(const std::string& folder, const tinygltf::Model& model, S32 texture_index)
 {
-    LLViewerFetchedTexture* ret = nullptr;
-
     const tinygltf::Image* image = get_image_from_texture_index(model, texture_index);
 
+    LLImageRaw* rawImage = nullptr;
+
     if (image != nullptr && 
         image->bits == 8 &&
         !image->image.empty() &&
         image->component <= 4)
     {
-        LLPointer<LLImageRaw> rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
+        rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
         rawImage->verticalFlip();
-        
-        ret = LLViewerTextureManager::getFetchedTexture(rawImage, FTType::FTT_LOCAL_FILE, true);
+    }
+
+    return rawImage;
+}
 
-        ret->forceToSaveRawImage(0, F32_MAX);
+static void strip_alpha_channel(LLPointer<LLImageRaw>& img)
+{
+    if (img->getComponents() == 4)
+    {
+        LLImageRaw* tmp = new LLImageRaw(img->getWidth(), img->getHeight(), 3);
+        tmp->copyUnscaled4onto3(img);
+        img = tmp;
     }
+}
 
-    // TODO: provide helpful error message if image fails to load
+// copy red channel from src_img to dst_img
+// PRECONDITIONS:
+// dst_img must be 3 component
+// src_img and dst_image must have the same dimensions
+static void copy_red_channel(LLPointer<LLImageRaw>& src_img, LLPointer<LLImageRaw>& dst_img)
+{
+    llassert(src_img->getWidth() == dst_img->getWidth() && src_img->getHeight() == dst_img->getHeight());
+    llassert(dst_img->getComponents() == 3);
 
-    return ret;
+    U32 pixel_count = dst_img->getWidth() * dst_img->getHeight();
+    U8* src = src_img->getData();
+    U8* dst = dst_img->getData();
+    S8 src_components = src_img->getComponents();
+
+    for (U32 i = 0; i < pixel_count; ++i)
+    {
+        dst[i * 3] = src[i * src_components];
+    }
+}
+
+static void pack_textures(tinygltf::Model& model, tinygltf::Material& material, 
+    LLPointer<LLImageRaw>& albedo_img,
+    LLPointer<LLImageRaw>& normal_img,
+    LLPointer<LLImageRaw>& mr_img,
+    LLPointer<LLImageRaw>& emissive_img,
+    LLPointer<LLImageRaw>& occlusion_img,
+    LLPointer<LLViewerFetchedTexture>& albedo_tex,
+    LLPointer<LLViewerFetchedTexture>& normal_tex,
+    LLPointer<LLViewerFetchedTexture>& mr_tex,
+    LLPointer<LLViewerFetchedTexture>& emissive_tex)
+{
+    // TODO: downscale if needed
+    if (albedo_img)
+    {
+        albedo_tex = LLViewerTextureManager::getFetchedTexture(albedo_img, FTType::FTT_LOCAL_FILE, true);
+    }
+
+    if (normal_img)
+    {
+        strip_alpha_channel(normal_img);
+        normal_tex = LLViewerTextureManager::getFetchedTexture(normal_img, FTType::FTT_LOCAL_FILE, true);
+    }
+
+    if (mr_img)
+    {
+        strip_alpha_channel(mr_img);
+
+        if (occlusion_img && material.pbrMetallicRoughness.metallicRoughnessTexture.index != material.occlusionTexture.index)
+        {
+            // occlusion is a distinct texture from pbrMetallicRoughness
+            // pack into mr red channel
+            int occlusion_idx = material.occlusionTexture.index;
+            int mr_idx = material.pbrMetallicRoughness.metallicRoughnessTexture.index;
+            if (occlusion_idx != mr_idx)
+            {
+                //scale occlusion image to match resolution of mr image
+                occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight());
+             
+                copy_red_channel(occlusion_img, mr_img);
+            }
+        }
+    }
+    else if (occlusion_img)
+    {
+        //no mr but occlusion exists, make a white mr_img and copy occlusion red channel over
+        mr_img = new LLImageRaw(occlusion_img->getWidth(), occlusion_img->getHeight(), 3);
+        mr_img->clear(255, 255, 255);
+        copy_red_channel(occlusion_img, mr_img);
+
+    }
+
+    if (mr_img)
+    {
+        mr_tex = LLViewerTextureManager::getFetchedTexture(mr_img, FTType::FTT_LOCAL_FILE, true);
+    }
+
+    if (emissive_img)
+    {
+        strip_alpha_channel(emissive_img);
+        emissive_tex = LLViewerTextureManager::getFetchedTexture(emissive_img, FTType::FTT_LOCAL_FILE, true);
+    }
 }
 
 static LLColor4 get_color(const std::vector<double>& in)
@@ -423,13 +724,14 @@ void LLMaterialFilePicker::loadMaterial(const std::string& filename)
 
     if (!loaded)
     {
-        // TODO: show error_msg to user
+        LLNotificationsUtil::add("CannotUploadMaterial");
         return;
     }
 
     if (model_in.materials.empty())
     {
-        // TODO: show error message that materials are missing
+        // materials are missing
+        LLNotificationsUtil::add("CannotUploadMaterial");
         return;
     }
 
@@ -443,41 +745,56 @@ void LLMaterialFilePicker::loadMaterial(const std::string& filename)
     model_out.materials.resize(1);
 
     // get albedo texture
-    LLPointer<LLViewerFetchedTexture> albedo_tex = get_texture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index);
+    LLPointer<LLImageRaw> albedo_img = get_texture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index);
+    // get normal map
+    LLPointer<LLImageRaw> normal_img = get_texture(folder, model_in, material_in.normalTexture.index);
+    // get metallic-roughness texture
+    LLPointer<LLImageRaw> mr_img = get_texture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index);
+    // get emissive texture
+    LLPointer<LLImageRaw> emissive_img = get_texture(folder, model_in, material_in.emissiveTexture.index);
+    // get occlusion map if needed
+    LLPointer<LLImageRaw> occlusion_img;
+    if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index)
+    {
+        occlusion_img = get_texture(folder, model_in, material_in.occlusionTexture.index);
+    }
 
+    LLPointer<LLViewerFetchedTexture> albedo_tex;
+    LLPointer<LLViewerFetchedTexture> normal_tex;
+    LLPointer<LLViewerFetchedTexture> mr_tex;
+    LLPointer<LLViewerFetchedTexture> emissive_tex;
+    
+    pack_textures(model_in, material_in, albedo_img, normal_img, mr_img, emissive_img, occlusion_img,
+        albedo_tex, normal_tex, mr_tex, emissive_tex);
+    
     LLUUID albedo_id;
     if (albedo_tex != nullptr)
     {
+        albedo_tex->forceToSaveRawImage(0, F32_MAX);        
         albedo_id = albedo_tex->getID();
     }
 
-    // get metallic-roughness texture
-    LLPointer<LLViewerFetchedTexture> mr_tex = get_texture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index);
+    LLUUID normal_id;
+    if (normal_tex != nullptr)
+    {
+        normal_tex->forceToSaveRawImage(0, F32_MAX);
+        normal_id = normal_tex->getID();
+    }
 
     LLUUID mr_id;
     if (mr_tex != nullptr)
     {
+        mr_tex->forceToSaveRawImage(0, F32_MAX);
         mr_id = mr_tex->getID();
     }
 
-    // get emissive texture
-    LLPointer<LLViewerFetchedTexture> emissive_tex = get_texture(folder, model_in, material_in.emissiveTexture.index);
-
     LLUUID emissive_id;
     if (emissive_tex != nullptr)
     {
+        emissive_tex->forceToSaveRawImage(0, F32_MAX);
         emissive_id = emissive_tex->getID();
     }
 
-    // get normal map
-    LLPointer<LLViewerFetchedTexture> normal_tex = get_texture(folder, model_in, material_in.normalTexture.index);
-
-    LLUUID normal_id;
-    if (normal_tex != nullptr)
-    {
-        normal_id = normal_tex->getID();
-    }
-
     mME->setAlbedoId(albedo_id);
     mME->setMetallicRoughnessId(mr_id);
     mME->setEmissiveId(emissive_id);
@@ -491,9 +808,13 @@ void LLMaterialFilePicker::loadMaterial(const std::string& filename)
     
     mME->setMetalnessFactor(material_in.pbrMetallicRoughness.metallicFactor);
     mME->setRoughnessFactor(material_in.pbrMetallicRoughness.roughnessFactor);
-    
+
     mME->setDoubleSided(material_in.doubleSided);
 
+    std::string new_material = LLTrans::getString("New Material");
+    mME->setMaterialName(new_material);
+
+    mME->setHasUnsavedChanges(true);
     mME->openFloater();
 
     mME->applyToSelection();
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index e773ecd169cf4ed3ec53df9d4cfa7d291be9ef89..7f9c6c0b6350c1c07f23723e7b96dcf7c2d780dd 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -28,6 +28,8 @@
 
 #include "llfloater.h"
 
+class LLTextureCtrl;
+
 class LLMaterialEditor : public LLFloater
 {
 public:
@@ -40,9 +42,14 @@ class LLMaterialEditor : public LLFloater
     void applyToSelection();
 
     void onClickSave();
+    void onClickSaveAs();
+    void onSaveAsMsgCallback(const LLSD& notification, const LLSD& response);
+    void onClickCancel();
+    void onCancelMsgCallback(const LLSD& notification, const LLSD& response);
 
 	// llpanel
 	BOOL postBuild() override;
+    void onClickCloseBtn(bool app_quitting = false) override;
 
     LLUUID getAlbedoId();
     void setAlbedoId(const LLUUID& id);
@@ -59,6 +66,8 @@ class LLMaterialEditor : public LLFloater
 
     F32 getAlphaCutoff();
     void setAlphaCutoff(F32 alpha_cutoff);
+    
+    void setMaterialName(const std::string &name);
 
     LLUUID getMetallicRoughnessId();
     void setMetallicRoughnessId(const LLUUID& id);
@@ -80,5 +89,27 @@ class LLMaterialEditor : public LLFloater
 
     bool getDoubleSided();
     void setDoubleSided(bool double_sided);
+
+    void setHasUnsavedChanges(bool value);
+
+    void onCommitAlbedoTexture(LLUICtrl* ctrl, const LLSD& data);
+    void onCommitMetallicTexture(LLUICtrl* ctrl, const LLSD& data);
+    void onCommitEmissiveTexture(LLUICtrl* ctrl, const LLSD& data);
+    void onCommitNormalTexture(LLUICtrl* ctrl, const LLSD& data);
+
+private:
+    LLTextureCtrl* mAlbedoTextureCtrl;
+    LLTextureCtrl* mMetallicTextureCtrl;
+    LLTextureCtrl* mEmissiveTextureCtrl;
+    LLTextureCtrl* mNormalTextureCtrl;
+
+    // 'Default' texture, unless it's null or from inventory is the one with the fee
+    LLUUID mAlbedoTextureUploadId;
+    LLUUID mMetallicTextureUploadId;
+    LLUUID mEmissiveTextureUploadId;
+    LLUUID mNormalTextureUploadId;
+
+    bool mHasUnsavedChanges;
+    std::string mMaterialName;
 };
 
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 89256b40c4c79f5406ec2b51b4310e595058b3bb..49562da3f7d4f664b7e73693255775eb00e38aa4 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -919,6 +919,7 @@ void LLFloaterInventoryFinder::updateElementsFromFilter()
 	getChild<LLUICtrl>("check_clothing")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE));
 	getChild<LLUICtrl>("check_gesture")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE));
 	getChild<LLUICtrl>("check_landmark")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK));
+	getChild<LLUICtrl>("check_material")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_MATERIAL));
 	getChild<LLUICtrl>("check_notecard")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD));
 	getChild<LLUICtrl>("check_object")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT));
 	getChild<LLUICtrl>("check_script")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LSL));
@@ -975,6 +976,12 @@ void LLFloaterInventoryFinder::draw()
 		filtered_by_all_types = FALSE;
 	}
 
+	if (!getChild<LLUICtrl>("check_material")->getValue())
+	{
+		filter &= ~(0x1 << LLInventoryType::IT_MATERIAL);
+		filtered_by_all_types = FALSE;
+	}
+
 	if (!getChild<LLUICtrl>("check_notecard")->getValue())
 	{
 		filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);
@@ -1129,6 +1136,7 @@ void LLFloaterInventoryFinder::selectAllTypes(void* user_data)
 	self->getChild<LLUICtrl>("check_clothing")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_gesture")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_landmark")->setValue(TRUE);
+	self->getChild<LLUICtrl>("check_material")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_notecard")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_object")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_script")->setValue(TRUE);
@@ -1149,6 +1157,7 @@ void LLFloaterInventoryFinder::selectNoTypes(void* user_data)
 	self->getChild<LLUICtrl>("check_clothing")->setValue(FALSE);
 	self->getChild<LLUICtrl>("check_gesture")->setValue(FALSE);
 	self->getChild<LLUICtrl>("check_landmark")->setValue(FALSE);
+	self->getChild<LLUICtrl>("check_material")->setValue(FALSE);
 	self->getChild<LLUICtrl>("check_notecard")->setValue(FALSE);
 	self->getChild<LLUICtrl>("check_object")->setValue(FALSE);
 	self->getChild<LLUICtrl>("check_script")->setValue(FALSE);
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 1c4a56b549a78654afa0ffe93e9fad2f819ce43e..b761f34790c88f611380830942b2a453569a1084 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -193,10 +193,8 @@ void LLFloaterTexturePicker::setActive( BOOL active )
 void LLFloaterTexturePicker::setCanApplyImmediately(BOOL b)
 {
 	mCanApplyImmediately = b;
-	if (!mCanApplyImmediately)
-	{
-		getChild<LLUICtrl>("apply_immediate_check")->setValue(FALSE);
-	}
+
+	getChild<LLUICtrl>("apply_immediate_check")->setValue(mCanApplyImmediately);
 	updateFilterPermMask();
 }
 
@@ -413,11 +411,7 @@ BOOL LLFloaterTexturePicker::postBuild()
 
 	getChild<LLUICtrl>("apply_immediate_check")->setValue(gSavedSettings.getBOOL("TextureLivePreview"));
 	childSetCommitCallback("apply_immediate_check", onApplyImmediateCheck, this);
-
-	if (!mCanApplyImmediately)
-	{
-		getChildView("show_folders_check")->setEnabled(FALSE);
-	}
+    getChildView("apply_immediate_check")->setEnabled(mCanApplyImmediately);
 
 	getChild<LLUICtrl>("Pipette")->setCommitCallback( boost::bind(&LLFloaterTexturePicker::onBtnPipette, this));
 	childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this);
@@ -483,7 +477,7 @@ void LLFloaterTexturePicker::draw()
 		}
 
 		getChildView("Default")->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative);
-		getChildView("Blank")->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative);
+		getChildView("Blank")->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID != mDefaultImageAssetID) || mTentative);
 		getChildView("None")->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));
 
 		LLFloater::draw();
@@ -1146,7 +1140,7 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p)
 	mOnCloseCallback(NULL),
 	mOnSelectCallback(NULL),
 	mBorderColor( p.border_color() ),
-	mAllowNoTexture( FALSE ),
+	mAllowNoTexture( p.allow_no_texture ),
 	mAllowLocalTexture( TRUE ),
 	mImmediateFilterPermMask( PERM_NONE ),
 	mNonImmediateFilterPermMask( PERM_NONE ),
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index 92f6f89af62ab1a947a795f403adc4bbe4146867..1475c8c6fc5aea35d5518f400c5bcce547a5f25e 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -91,7 +91,7 @@ class LLTextureCtrl
 		:	image_id("image"),
 			default_image_id("default_image_id"),
 			default_image_name("default_image_name"),
-			allow_no_texture("allow_no_texture"),
+			allow_no_texture("allow_no_texture", false),
 			can_apply_immediately("can_apply_immediately"),
 			no_commit_on_selection("no_commit_on_selection", false),
 		    label_width("label_width", -1),
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 49eba9856ce70aead47e59d283011c256b001578..e76f0b36ee42d6b011b5c331c40a96a0bd1740ed 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -1650,6 +1650,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
         gDeferredPBROpaqueProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
         gDeferredPBROpaqueProgram.addPermutation("HAS_NORMAL_MAP", "1");
         gDeferredPBROpaqueProgram.addPermutation("HAS_SPECULAR_MAP", "1");
+        gDeferredPBROpaqueProgram.addPermutation("HAS_EMISSIVE_MAP", "1");
         gDeferredPBROpaqueProgram.addPermutation("DIFFUSE_ALPHA_MODE", "0");
 
         success = make_rigged_variant(gDeferredPBROpaqueProgram, gDeferredSkinnedPBROpaqueProgram);
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 82a5b285762a1a9d86f520f54e3c85e4829466cd..ac9c3854050699432d1261f0a862acfe02e0ff3b 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -372,8 +372,12 @@ void validate_framebuffer_object();
 // for_impostor -- whether or not these render targets are for an impostor (if true, avoids implicit sRGB conversions)
 bool addDeferredAttachments(LLRenderTarget& target, bool for_impostor = false)
 {
-	return target.addColorAttachment(for_impostor ? GL_RGBA : GL_SRGB8_ALPHA8) && //specular
-			target.addColorAttachment(GL_RGB10_A2); //normal+z
+    bool pbr = gSavedSettings.getBOOL("RenderPBR");
+    bool valid = true
+        && target.addColorAttachment(for_impostor ? GL_RGBA : GL_SRGB8_ALPHA8) // frag-data[1] specular or PBR packed OcclusionRoughnessMetal
+        && target.addColorAttachment(GL_RGB10_A2)                              // frag_data[2] normal+z+fogmask, See: class1\deferred\materialF.glsl & softenlight
+        && (pbr ? target.addColorAttachment(GL_RGBA) : true);                  // frag_data[3] emissive
+    return valid;
 }
 
 LLPipeline::LLPipeline() :
diff --git a/indra/newview/skins/default/textures/icons/Inv_Material.png b/indra/newview/skins/default/textures/icons/Inv_Material.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5918ceaedc671003c17715cee7f5ddf340020cd
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Inv_Material.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index a36b859b6c0e2374aea0a7aeee5c447fd931c82d..b0ae5fe447e3e77bdfd42e2d7cbafaa6d2bc0cb2 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -303,6 +303,7 @@ with the same filename but different name
   <texture name="Inv_LostClosed" file_name="icons/Inv_LostClosed.png" preload="false" />
   <texture name="Inv_LostOpen" file_name="icons/Inv_LostOpen.png" preload="false" />
   <texture name="Inv_Landmark" file_name="icons/Inv_Landmark.png" preload="false" />
+  <texture name="Inv_Material" file_name="icons/Inv_Material.png" preload="false" />
   <texture name="Inv_Mesh" file_name="icons/Inv_Mesh.png" preload="false" />  
   <texture name="Inv_Notecard" file_name="icons/Inv_Notecard.png" preload="false" />
   <texture name="Inv_Object" file_name="icons/Inv_Object.png" preload="false" />
diff --git a/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml b/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml
index d783d1e23c513d060daf3955052644400b88e585..e91efb89b2df2df52251c6ee591bfa9da3f72e08 100644
--- a/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml
+++ b/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml
@@ -2,7 +2,7 @@
 <floater
  legacy_header_height="18"
  can_minimize="false"
- height="466"
+ height="486"
  layout="topleft"
  name="Inventory Finder"
  help_topic="inventory_finder"
@@ -93,6 +93,23 @@
      name="check_landmark"
      top_delta="0"
      width="126" />
+    <icon
+     height="16"
+     image_name="Inv_Material"
+     layout="topleft"
+     left="8"
+     mouse_opaque="true"
+     name="icon_material"
+     top="122"
+     width="16" />
+    <check_box
+     height="16"
+     label="Materials"
+     layout="topleft"
+     left_pad="2"
+     name="check_material"
+     top_delta="0"
+     width="126" />
     <icon
      height="16"
      image_name="Inv_Notecard"
@@ -100,7 +117,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_notecard"
-     top="122"
+     top="142"
      width="16" />
     <check_box
      height="16"
@@ -117,7 +134,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_object"
-     top="142"
+     top="162"
      width="16" />
     <check_box
      height="16"
@@ -134,7 +151,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_script"
-     top="162"
+     top="182"
      width="16" />
     <check_box
      height="16"
@@ -151,7 +168,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_sound"
-     top="182"
+     top="202"
      width="16" />
     <check_box
      height="16"
@@ -168,7 +185,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_texture"
-     top="202"
+     top="222"
      width="16" />
     <check_box
      height="16"
@@ -185,7 +202,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_snapshot"
-     top="222"
+     top="242"
      width="16" />
     <check_box
      height="16"
@@ -202,7 +219,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_settings"
-     top="242"
+     top="262"
      width="16" />
     <check_box
      height="16"
@@ -220,7 +237,7 @@
      layout="topleft"
      left="8"
      name="All"
-     top="262"
+     top="282"
      width="100" />
     <button
      height="20"
@@ -274,7 +291,7 @@
      width="260"/>
     <check_box
      height="16"
-     top="352"
+     top="372"
      label="Since Logoff"
      layout="topleft"
      left_delta="0"
@@ -290,7 +307,7 @@
      layout="topleft"
      left_delta="0"
      name="- OR -"
-     top="370"
+     top="390"
      width="144">
         - OR -
     </text>
@@ -298,7 +315,7 @@
      height="16"
      layout="topleft"
      name="date_search_direction"
-     top="388"
+     top="408"
      left="8"
      width="270">
      <radio_item
@@ -368,6 +385,6 @@
      layout="topleft"
      name="Close"
      right="-6"
-     top="434"
+     top="454"
      width="76" />
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_material_editor.xml b/indra/newview/skins/default/xui/en/floater_material_editor.xml
index 5d72e6f79ddc6d8f11c64d193cf866ef7c781847..36315d3b04ed3a7c004bb4e3e1bf526461b83e61 100644
--- a/indra/newview/skins/default/xui/en/floater_material_editor.xml
+++ b/indra/newview/skins/default/xui/en/floater_material_editor.xml
@@ -3,12 +3,22 @@
  legacy_header_height="18"
  can_resize="false"
  default_tab_group="1"
- height="777"
+ height="887"
  layout="topleft"
  name="material editor"
  help_topic="material_editor"
- title="Material: [MATERIAL_NAME]"
+ title="[MATERIAL_NAME]"
  width="256">
+  <string name="no_upload_fee_string">no upload fee</string>
+  <string name="upload_fee_string">L$10 upload fee</string>
+  <check_box
+          follows="left|top"
+          label="Double Sided"
+          left="14"
+          top="14"
+          name="double sided"
+          height="25"
+          width="120" />
   <panel
     border="true"
          follows="left|top"
@@ -17,11 +27,12 @@
          layout="topleft"
          left="5"
          mouse_opaque="false"
-         name="Texture"
-         top="20"
+         name="albedo_texture_pnl"
+         top_pad="5"
          >
   <text
              type="string"
+             font.style="BOLD"
              length="1"
              follows="left|top"
              height="10"
@@ -31,18 +42,33 @@
              width="64">
     Albedo:
   </text>
-  <texture_picker
-             can_apply_immediately="true"
+    <texture_picker
+             can_apply_immediately="false"
              default_image_name="Default"
              fallback_image="materials_ui_x_24.png"
+             allow_no_texture="true"
              follows="left|top"
              top_pad="8"
              height="151"
              layout="topleft"
              left="10"
-             name="albedo texture"
+             name="albedo_texture"
              tool_tip="Albedo map.  Alpha channel is optional and used for transparency."
              width="128" />
+    <text
+               type="string"
+               font.style="BOLD"
+               length="1"
+               follows="left|top"
+               height="10"
+               width="128"
+               layout="topleft"
+               left="10"
+               top_pad="-17"
+               name="albedo_upload_fee"
+             >
+      No upload fee
+    </text>
   <text
              type="string"
              length="1"
@@ -50,12 +76,12 @@
              height="10"
              layout="topleft"
              left_pad="5"
-             top_delta="-15"
+             top="8"
              >
     Tint
   </text>
   <color_swatch
-                    can_apply_immediately="true"
+                    can_apply_immediately="false"
                     follows="left|top"
                     height="40"
                     label_height="0"
@@ -152,37 +178,53 @@
     border="true"
          follows="left|top"
          width="246"
-         height="160"
+         height="175"
          layout="topleft"
          left="5"
          mouse_opaque="false"
-         name="Texture"
+         name="metallic_texture_pnl"
          top_pad="5"
          >
     <text
              type="string"
+             font.style="BOLD"
              length="1"
              follows="left|top"
              height="10"
              layout="topleft"
              left="10"
-             top_pad="5"
+             top="5"
              >
     Metallic-Roughness:
   </text>
-  <texture_picker
-             can_apply_immediately="true"
+    <texture_picker
+             can_apply_immediately="false"
              default_image_name="Default"
              fallback_image="materials_ui_x_24.png"
+             allow_no_texture="true"
              follows="left|top"
              width="128" 
              height="151"
              layout="topleft"
              left="10"
-             name="metallic-roughness texture"
+             name="metallic_roughness_texture"
              tool_tip="GLTF metallic-roughness map with optional occlusion.  Red channel is occlusion, green channel is roughness, blue channel is metalness."
              top_pad="8"
              />
+  <text
+             type="string"
+             font.style="BOLD"
+             length="1"
+             follows="left|top"
+             height="10"
+             width="128"
+             layout="topleft"
+             left="10"
+             top_pad="-17"
+             name="metallic_upload_fee"
+           >
+    No upload fee
+  </text>
   <text
              type="string"
              length="1"
@@ -190,7 +232,7 @@
              height="10"
              layout="topleft"
              left_pad="5"
-             top_delta="-15"
+             top="8"
              >
     Metallic Factor
   </text>
@@ -237,15 +279,16 @@
     border="true"
          follows="left|top"
          width="246"
-         height="160"
+         height="175"
          layout="topleft"
          left="5"
          mouse_opaque="false"
-         name="Texture"
+         name="emissive_texture_pnl"
          top_pad="5"
          >
     <text
                type="string"
+               font.style="BOLD"
                length="1"
                follows="left|top"
                height="10"
@@ -256,16 +299,31 @@
       Emissive:
     </text>
     <texture_picker
-               can_apply_immediately="true"
+               can_apply_immediately="false"
                default_image_name="Default"
                fallback_image="materials_ui_x_24.png"
+               allow_no_texture="true"
                follows="left|top"
                top_pad="8"
                height="151"
                layout="topleft"
                left="10"
-               name="emissive texture"
+               name="emissive_texture"
                width="128" />
+    <text
+               type="string"
+               font.style="BOLD"
+               length="1"
+               follows="left|top"
+               height="10"
+               width="128"
+               layout="topleft"
+               left="10"
+               top_pad="-17"
+               name="emissive_upload_fee"
+           >
+      No upload fee
+    </text>
     <text
                type="string"
                length="1"
@@ -273,12 +331,12 @@
                height="10"
                layout="topleft"
                left_pad="5"
-               top_delta="-15"
+               top="8"
              >
       Tint
     </text>
     <color_swatch
-                      can_apply_immediately="true"
+                      can_apply_immediately="false"
                       follows="left|top"
                       height="40"
                       label_height="0"
@@ -316,14 +374,16 @@
     border="true"
          follows="left|top"
          width="246"
-         height="160"
+         height="175"
          layout="topleft"
          left="5"
          mouse_opaque="false"
          top_pad="5"
+         name="normal_texture_pnl"
          >
     <text
                type="string"
+               font.style="BOLD"
                length="1"
                follows="left|top"
                height="10"
@@ -334,41 +394,106 @@
       Normal:
     </text>
     <texture_picker
-               can_apply_immediately="true"
+               can_apply_immediately="false"
                default_image_name="Default"
                fallback_image="materials_ui_x_24.png"
+               allow_no_texture="true"
                follows="left|top"
                top_pad="8"
                height="151"
                layout="topleft"
                left="10"
-               name="normal texture"
+               name="normal_texture"
                width="128" />
-    <!--<check_box
-          follows="left|top"
-          label="Mikkt Space"
-          left_pad="10"
-          top_delta="0"
-          height="25"
-          width="120" />-->
+    <text
+               type="string"
+               font.style="BOLD"
+               length="1"
+               follows="left|top"
+               height="10"
+               width="128"
+               layout="topleft"
+               left="10"
+               top_pad="-17"
+               name="normal_upload_fee"
+           >
+      No upload fee
+    </text>
+  </panel>
+
+  <panel
+         follows="right|bottom"
+         width="246"
+         height="97"
+         layout="bottomright"
+         top_delta="-2"
+         left="5"
+         name="button_panel"
+         >
+    <text
+               type="string"
+               name="unsaved_changes"
+               font.style="BOLD"
+               text_color="DrYellow"
+               length="1"
+               follows="left|top"
+               height="10"
+               width="200"
+               layout="topleft"
+               left="10"
+               top="0"
+           >
+      Usaved changes
+    </text>
+    <button
+               follows="left|top"
+               height="25"
+               label="Save"
+               layout="topleft"
+               name="save"
+               top_pad="7"
+               left="0"
+               width="120" />
+    <button
+               follows="left|top"
+               height="25"
+               label="Save As..."
+               layout="topleft"
+               name="save_as"
+               top_delta="0"
+               left_pad="6"
+               width="120" />
+    <text
+               type="string"
+               font.style="BOLD"
+               length="1"
+               follows="left|top"
+               height="10"
+               width="220"
+               layout="topleft"
+               left="10"
+               top_pad="5"
+           >
+      Total upload fee: L$ [FEE]
+    </text>
+    
+    <view_border
+        bevel_style="none"
+        height="0"
+        layout="topleft"
+        left="0"
+        name="button_border"
+        top_pad="7"
+        width="246"/>
+    
+    <button
+               follows="left|top"
+               height="25"
+               label="Cancel"
+               layout="topleft"
+               name="cancel"
+               top_pad="7"
+               left="61"
+               width="121" />
   </panel>
-  <check_box
-          follows="left|top"
-          label="Double Sided"
-          left="5"
-          top_pad="5"
-          name="double sided"
-          height="25"
-          width="120" />
-  <button
-             follows="right|bottom"
-             height="25"
-             label="Save"
-             layout="bottomright"
-             name="save"
-             tool_tip="Browse for an editor (executable) to edit floater XML files"
-             top_delta="-2"
-             left="5"
-             width="246" />
-  
 </floater>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 786cf32e7a99d589077d5cae28f42e3fb3f72298..0ca3e043e727f692959e6663c1e2d464b388716f 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -9025,7 +9025,64 @@ Unable to upload texture.
 [REASON]   
   <tag>fail</tag>  
   </notification>
-  
+
+  <notification
+   icon="alertmodal.tga"
+   name="CannotUploadMaterial"
+   type="alertmodal">
+There was a problem uploading the file
+    <tag>fail</tag>
+  </notification>
+
+  <notification
+ icon="alertmodal.tga"
+ label="Save Material"
+ name="SaveMaterialAs"
+ type="alertmodal">
+    <unique/>
+    Name this material:
+    <tag>confirm</tag>
+    <form name="form">
+      <input name="message" type="text">
+        [DESC]
+      </input>
+      <button
+       default="true"
+       index="0"
+       name="OK"
+       text="OK"/>
+      <button
+       index="1"
+       name="Cancel"
+       text="Cancel"/>
+    </form>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="InvalidMaterialName"
+   type="alertmodal">
+Please enter a non-empty name
+    <tag>fail</tag>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="UsavedMaterialChanges"
+   type="alertmodal">
+    You have unsaved changes.
+    <form name="form">
+      <button
+       index="0"
+       name="discard"
+       text="Discard changes"/>
+      <button
+       index="1"
+       name="keep"
+       text="Keep editing"/>
+    </form>
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="LivePreviewUnavailable"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 0866f29355a4afea833c3f01a6c34592c3d731e7..e1678f418f9dea033e80315cc1333bc8251ce08f 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -2414,6 +2414,7 @@ If you continue to receive this message, please contact Second Life support for
 	<string name="Clothing"      value=" Clothing," />
 	<string name="Gestures"      value=" Gestures," />
 	<string name="Landmarks"     value=" Landmarks," />
+	<string name="Materials"     value=" Materials," />
 	<string name="Notecards"     value=" Notecards," />
 	<string name="Objects"       value=" Objects," />
 	<string name="Scripts"       value=" Scripts," />