diff --git a/autobuild.xml b/autobuild.xml
index c90ffe493cc8b6b16162145127868fb126301bc2..7652b95d9638a6deb4a0c928b5b356aa595d4524 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -45,6 +45,36 @@
         <key>version</key>
         <string>1.2.15</string>
       </map>
+      <key>libatmosphere</key>
+      <map>
+        <key>copyright</key>
+        <string>Copyright © 2017 Eric Bruneton.</string>
+        <key>description</key>
+        <string>Precomputed multiple atmospheric scattering library.</string>
+        <key>license</key>
+        <string>BSD</string>
+        <key>license_file</key>
+        <string>LICENSES/libatmosphere.txt</string>
+        <key>name</key>
+        <string>libatmosphere</string>
+        <key>platforms</key>
+        <map>
+          <key>windows64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+	      <string>1df4dcf1581c3a7eae267d100ddd4b3b</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/100/1371370/libatmosphere-1.0.0-100-windows64-133713370.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>1.4.5.504800</string>
+      </map>
       <key>apr_suite</key>
       <map>
         <key>copyright</key>
@@ -1694,9 +1724,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>7b769c4284bdbd5fce536395d1eab695</string>
+              <string>1463e6a7c6aa65bc65292bd7e48c107e</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4054/11304/kdu-7.9.1.504041-darwin-504041.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/13675/84961/kdu-7.A.3.512635-darwin-512635.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin</string>
@@ -1706,9 +1736,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>a48db5cf79a4631090bfc968572d9953</string>
+              <string>d3ef7a02b7b765355096ba66085433be</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4052/11292/kdu-7.9.1.504041-darwin64-504041.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/13674/84957/kdu-7.A.3.512635-darwin64-512635.tar.bz2</string>              
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -1718,9 +1748,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>ed952c0cb86329e63a8db190953962d8</string>
+              <string>78c94809fe2c4ce9323cb2d4a0d1b105</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/kdu_3p-update-kdu/rev/296932/arch/Linux/installer/kdu-7.2.296932-linux-296932.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/13672/84968/kdu-7.A.3.512635-linux-512635.tar.bz2</string>              
             </map>
             <key>name</key>
             <string>linux</string>
@@ -1730,9 +1760,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>99b0b22f87cebdd02c4cc954a7b3b465</string>
+              <string>87f9381c1da1148985a9f5ef3e09aeba</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4053/11298/kdu-7.9.1.504041-linux64-504041.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/13673/84974/kdu-7.A.3.512635-linux64-512635.tar.bz2</string>              
             </map>
             <key>name</key>
             <string>linux64</string>
@@ -1742,9 +1772,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>f3ff5982b3b5f02738044432dd77a2c1</string>
+              <string>e570aaf7e77dec7538f68d1bcfbc5abe</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4055/11310/kdu-7.9.1.504041-windows-504041.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/13677/84986/kdu-7.A.3.512635-windows-512635.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -1754,9 +1784,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>3010fa35f412b36296342b07de06f1ca</string>
+              <string>f8ed3aaf6b3f840a5bf2819e5e931cac</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4056/11316/kdu-7.9.1.504041-windows64-504041.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/13676/84980/kdu-7.A.3.512635-windows64-512635.tar.bz2</string>              
             </map>
             <key>name</key>
             <string>windows64</string>
diff --git a/indra/cmake/LLRender.cmake b/indra/cmake/LLRender.cmake
index 868922451f0008135f92ce7f97abdea9ef53fcbe..0940d1de635dc234eb5cd0f2499e75764d007a54 100644
--- a/indra/cmake/LLRender.cmake
+++ b/indra/cmake/LLRender.cmake
@@ -3,6 +3,7 @@
 include(Variables)
 include(FreeType)
 include(GLH)
+include(Atmosphere)
 
 set(LLRENDER_INCLUDE_DIRS
     ${LIBS_OPEN_DIR}/llrender
diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp
index 2cf86bb4feca68eded411656a0e8870c53902c7a..3dbab22de161e53efb2ede16df38fe3876064c8a 100644
--- a/indra/llappearance/lltexlayer.cpp
+++ b/indra/llappearance/lltexlayer.cpp
@@ -1578,7 +1578,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
 			alpha_data = new U8[width * height];
 			mAlphaCache[cache_index] = alpha_data;
 			glReadPixels(x, y, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_data);
-		}
+            }
 		
 		getTexLayerSet()->getAvatarAppearance()->dirtyMesh();
 
diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp
index dcef2b345eac9de9c7da63211d477d67d57633f4..a8d9eba2a0f66a7e5e36ca0bb0b1a1a4a7fef662 100644
--- a/indra/llmath/llquaternion.cpp
+++ b/indra/llmath/llquaternion.cpp
@@ -864,6 +864,26 @@ void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const
 	}
 }
 
+const LLQuaternion& LLQuaternion::setFromAzimuthAndAltitude(F32 azimuthRadians, F32 altitudeRadians)
+{
+    // euler angle inputs are complements of azimuth/altitude which are measured from zenith
+    F32 pitch = llclamp(F_PI_BY_TWO - altitudeRadians, 0.0f, F_PI_BY_TWO);
+    F32 yaw   = llclamp(F_PI_BY_TWO - azimuthRadians,  0.0f, F_PI_BY_TWO);
+    setEulerAngles(0.0f, pitch, yaw);
+    return *this;
+}
+
+void LLQuaternion::getAzimuthAndAltitude(F32 &azimuthRadians, F32 &altitudeRadians)
+{
+    F32 rick_roll;
+    F32 pitch;
+    F32 yaw;
+    getEulerAngles(&rick_roll, &pitch, &yaw);
+    // make these measured from zenith
+    altitudeRadians = llclamp(F_PI_BY_TWO - pitch, 0.0f, F_PI_BY_TWO);
+    azimuthRadians  = llclamp(F_PI_BY_TWO - yaw,   0.0f, F_PI_BY_TWO);
+}
+
 // quaternion does not need to be normalized
 void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
 {
diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h
index 11b6abf80052d06c10f995ba8831b10f2f452200..e2cdad548b73b7794064a5c4c796eb5ace922ee6 100644
--- a/indra/llmath/llquaternion.h
+++ b/indra/llmath/llquaternion.h
@@ -84,7 +84,8 @@ class LLQuaternion
 	const LLQuaternion&	set(const F32 *q);						// Sets Quaternion to normalize(quat[VX], quat[VY], quat[VZ], quat[VW])
 	const LLQuaternion&	set(const LLMatrix3 &mat);				// Sets Quaternion to mat2quat(mat)
 	const LLQuaternion&	set(const LLMatrix4 &mat);				// Sets Quaternion to mat2quat(mat)
-
+    const LLQuaternion& setFromAzimuthAndAltitude(F32 azimuth, F32 altitude);
+    
 	const LLQuaternion&	setAngleAxis(F32 angle, F32 x, F32 y, F32 z);	// Sets Quaternion to axis_angle2quat(angle, x, y, z)
 	const LLQuaternion&	setAngleAxis(F32 angle, const LLVector3 &vec);	// Sets Quaternion to axis_angle2quat(angle, vec)
 	const LLQuaternion&	setAngleAxis(F32 angle, const LLVector4 &vec);	// Sets Quaternion to axis_angle2quat(angle, vec)
@@ -105,6 +106,7 @@ class LLQuaternion
 	void		getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const;	// returns rotation in radians about axis x,y,z
 	void		getAngleAxis(F32* angle, LLVector3 &vec) const;
 	void		getEulerAngles(F32 *roll, F32* pitch, F32 *yaw) const;
+    void        getAzimuthAndAltitude(F32 &azimuth, F32 &altitude);
 
 	F32	normalize();	// Normalizes Quaternion and returns magnitude
 	F32	normQuat();		// deprecated
diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp
index e24d222cb66e8eaf7063e1a4c785c74dcf7f7544..594793cf562d126699cb0a9acf57b1c1518868c8 100644
--- a/indra/llplugin/llpluginprocesschild.cpp
+++ b/indra/llplugin/llpluginprocesschild.cpp
@@ -253,7 +253,7 @@ void LLPluginProcessChild::sleep(F64 seconds)
 	}
 	else
 	{
-		ms_sleep((int)(seconds * 1000.0f));
+    ms_sleep((int)(seconds * 1000.0f));
 	}
 }
 
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt
index 331f988382a3476edb77981d890c20dbb97b1279..89451a072cab4dc662f9e533177e1948545100ec 100644
--- a/indra/llrender/CMakeLists.txt
+++ b/indra/llrender/CMakeLists.txt
@@ -13,6 +13,7 @@ include(LLVFS)
 include(LLWindow)
 include(LLXML)
 include(LLVFS)
+include(Atmosphere)
 
 include_directories(
     ${FREETYPE_INCLUDE_DIRS}
@@ -24,6 +25,7 @@ include_directories(
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     ${LLVFS_INCLUDE_DIRS}
+    ${ATMOSPHERE_INCLUDE_DIR}
     )
 include_directories(SYSTEM
     ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -31,6 +33,7 @@ include_directories(SYSTEM
     )
 
 set(llrender_SOURCE_FILES
+    llatmosphere.cpp
     llcubemap.cpp
     llfontbitmapcache.cpp
     llfontfreetype.cpp
@@ -56,6 +59,7 @@ set(llrender_SOURCE_FILES
 set(llrender_HEADER_FILES
     CMakeLists.txt
 
+    llatmosphere.h
     llcubemap.h
     llfontgl.h
     llfontfreetype.h
@@ -126,5 +130,6 @@ target_link_libraries(llrender
     ${LLVFS_LIBRARIES}
     ${LLWINDOW_LIBRARIES}
     ${FREETYPE_LIBRARIES}
-    ${OPENGL_LIBRARIES})
+    ${OPENGL_LIBRARIES}
+    ${ATMOSPHERE_LIBRARIES})
 
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 155c2402bd037d8e4e72d8258e4f60b853fd6a93..2f78b6e104f1540ba6075addddabbdc45bd43234 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -1348,8 +1348,19 @@ void LLGLManager::initExtensions()
 	if (mHasVertexShader)
 	{
 		LL_INFOS() << "initExtensions() VertexShader-related procs..." << LL_ENDL;
-		glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocationARB");
-		glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocationARB");
+
+        // nSight doesn't support use of ARB funcs that have been normalized in the API
+        if (!LLRender::sNsightDebugSupport)
+        {
+		    glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocationARB");
+		    glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocationARB");
+        }
+        else
+        {
+            glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocation");
+            glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocation");
+        }
+
 		glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttribARB");
 		glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dARB");
 		glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dvARB");
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index bba94a976fc84ba5383f044aeb7e172a07418d24..b09ec53bc035f641c4901017602c999f93df5efb 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -401,6 +401,11 @@ BOOL LLGLSLShader::createShader(std::vector<LLStaticHashedString> * attributes,
     mDefines["OLD_SELECT"] = "1";
 #endif
     
+    if (mExtraLinkObject)
+    {
+        attachObject(mExtraLinkObject);
+    }
+
     //compile new source
     vector< pair<string,GLenum> >::iterator fileIter = mShaderFiles.begin();
     for ( ; fileIter != mShaderFiles.end(); fileIter++ )
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 8e9c29ea4cd80e2d7cbed6c385e6a25908f3947c..a7a9e27fcd7d58f9c6a22ab0e1ddbc22315f1e65 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -205,6 +205,8 @@ class LLGLSLShader
 	bool mTextureStateFetched;
     magmin_filter_t mTextureMagMinFilter;
 
+    GLhandleARB mExtraLinkObject = 0;
+
 private:
 	void unloadInternal();
 
diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index 3a6eebebbafef5b399376fce6dc1e16fa6c25f83..c0e5477cea0f6e852b0a3538c8eeaf14d0fc6382 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -30,20 +30,20 @@
 // static
 S32 LLGLTexture::getTotalNumOfCategories() 
 {
-	return MAX_GL_IMAGE_CATEGORY - (BOOST_HIGH - BOOST_SCULPTED) + 2 ;
+	return MAX_GL_IMAGE_CATEGORY - (BOOST_HIGH - BOOST_SCULPTED) + 2;
 }
 
 // static
 //index starts from zero.
 S32 LLGLTexture::getIndexFromCategory(S32 category) 
 {
-	return (category < BOOST_HIGH) ? category : category - (BOOST_HIGH - BOOST_SCULPTED) + 1 ;
+	return (category < BOOST_HIGH) ? category : category - (BOOST_HIGH - BOOST_SCULPTED) + 1;
 }
 
 //static 
 S32 LLGLTexture::getCategoryFromIndex(S32 index)
 {
-	return (index < BOOST_HIGH) ? index : index + (BOOST_HIGH - BOOST_SCULPTED) - 1 ;
+	return (index < BOOST_HIGH) ? index : index + (BOOST_HIGH - BOOST_SCULPTED) - 1;
 }
 
 LLGLTexture::LLGLTexture(BOOL usemipmaps)
@@ -55,19 +55,19 @@ LLGLTexture::LLGLTexture(BOOL usemipmaps)
 LLGLTexture::LLGLTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps)
 {
 	init();
-	mFullWidth = width ;
-	mFullHeight = height ;
+	mFullWidth = width;
+	mFullHeight = height;
 	mUseMipMaps = usemipmaps;
-	mComponents = components ;
+	mComponents = components;
 	setTexelsPerImage();
 }
 
 LLGLTexture::LLGLTexture(const LLImageRaw* raw, BOOL usemipmaps)
 {
 	init();
-	mUseMipMaps = usemipmaps ;
+	mUseMipMaps = usemipmaps;
 	// Create an empty image of the specified size and width
-	mGLTexturep = new LLImageGL(raw, usemipmaps) ;
+	mGLTexturep = new LLImageGL(raw, usemipmaps);
 }
 
 LLGLTexture::~LLGLTexture()
@@ -81,13 +81,13 @@ void LLGLTexture::init()
 
 	mFullWidth = 0;
 	mFullHeight = 0;
-	mTexelsPerImage = 0 ;
-	mUseMipMaps = FALSE ;
-	mComponents = 0 ;
+	mTexelsPerImage = 0;
+	mUseMipMaps = FALSE;
+	mComponents = 0;
 
-	mTextureState = NO_DELETE ;
+	mTextureState = NO_DELETE;
 	mDontDiscard = FALSE;
-	mNeedsGLTexture = FALSE ;
+	mNeedsGLTexture = FALSE;
 }
 
 void LLGLTexture::cleanup()
@@ -111,287 +111,301 @@ void LLGLTexture::setBoostLevel(S32 level)
 {
 	if(mBoostLevel != level)
 	{
-		mBoostLevel = level ;
+		mBoostLevel = level;
 		if(mBoostLevel != LLGLTexture::BOOST_NONE
 		   && mBoostLevel != LLGLTexture::BOOST_ICON)
 		{
-			setNoDelete() ;		
+			setNoDelete();		
 		}
 	}
 }
 
 void LLGLTexture::forceActive()
 {
-	mTextureState = ACTIVE ; 
+	mTextureState = ACTIVE; 
 }
 
 void LLGLTexture::setActive() 
 { 
 	if(mTextureState != NO_DELETE)
 	{
-		mTextureState = ACTIVE ; 
+		mTextureState = ACTIVE; 
 	}
 }
 
 //set the texture to stay in memory
 void LLGLTexture::setNoDelete() 
 { 
-	mTextureState = NO_DELETE ;
+	mTextureState = NO_DELETE;
 }
 
 void LLGLTexture::generateGLTexture() 
 {	
 	if(mGLTexturep.isNull())
 	{
-		mGLTexturep = new LLImageGL(mFullWidth, mFullHeight, mComponents, mUseMipMaps) ;
+		mGLTexturep = new LLImageGL(mFullWidth, mFullHeight, mComponents, mUseMipMaps);
 	}
 }
 
 LLImageGL* LLGLTexture::getGLTexture() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep ;
+	return mGLTexturep;
 }
 
 BOOL LLGLTexture::createGLTexture() 
 {
 	if(mGLTexturep.isNull())
 	{
-		generateGLTexture() ;
+		generateGLTexture();
 	}
 
-	return mGLTexturep->createGLTexture() ;
+	return mGLTexturep->createGLTexture();
 }
 
 BOOL LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, BOOL to_create, S32 category)
 {
-	llassert(mGLTexturep.notNull()) ;	
+	llassert(mGLTexturep.notNull());	
 
-	BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category) ;
+	BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category);
 
 	if(ret)
 	{
-		mFullWidth = mGLTexturep->getCurrentWidth() ;
-		mFullHeight = mGLTexturep->getCurrentHeight() ; 
-		mComponents = mGLTexturep->getComponents() ;	
+		mFullWidth = mGLTexturep->getCurrentWidth();
+		mFullHeight = mGLTexturep->getCurrentHeight(); 
+		mComponents = mGLTexturep->getComponents();	
 		setTexelsPerImage();
 	}
 
-	return ret ;
+	return ret;
 }
 
 void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes)
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 	
-	mGLTexturep->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes) ;
+	mGLTexturep->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes);
 }
 void LLGLTexture::setAddressMode(LLTexUnit::eTextureAddressMode mode)
 {
-	llassert(mGLTexturep.notNull()) ;
-	mGLTexturep->setAddressMode(mode) ;
+	llassert(mGLTexturep.notNull());
+	mGLTexturep->setAddressMode(mode);
 }
 void LLGLTexture::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
 {
-	llassert(mGLTexturep.notNull()) ;
-	mGLTexturep->setFilteringOption(option) ;
+	llassert(mGLTexturep.notNull());
+	mGLTexturep->setFilteringOption(option);
 }
 
 //virtual
 S32	LLGLTexture::getWidth(S32 discard_level) const
 {
-	llassert(mGLTexturep.notNull()) ;
-	return mGLTexturep->getWidth(discard_level) ;
+	llassert(mGLTexturep.notNull());
+	return mGLTexturep->getWidth(discard_level);
 }
 
 //virtual
 S32	LLGLTexture::getHeight(S32 discard_level) const
 {
-	llassert(mGLTexturep.notNull()) ;
-	return mGLTexturep->getHeight(discard_level) ;
+	llassert(mGLTexturep.notNull());
+	return mGLTexturep->getHeight(discard_level);
 }
 
 S32 LLGLTexture::getMaxDiscardLevel() const
 {
-	llassert(mGLTexturep.notNull()) ;
-	return mGLTexturep->getMaxDiscardLevel() ;
+	llassert(mGLTexturep.notNull());
+	return mGLTexturep->getMaxDiscardLevel();
 }
 S32 LLGLTexture::getDiscardLevel() const
 {
-	llassert(mGLTexturep.notNull()) ;
-	return mGLTexturep->getDiscardLevel() ;
+	llassert(mGLTexturep.notNull());
+	return mGLTexturep->getDiscardLevel();
 }
 S8  LLGLTexture::getComponents() const 
 { 
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 	
-	return mGLTexturep->getComponents() ;
+	return mGLTexturep->getComponents();
 }
 
 LLGLuint LLGLTexture::getTexName() const 
 { 
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getTexName() ; 
+	return mGLTexturep->getTexName(); 
 }
 
 BOOL LLGLTexture::hasGLTexture() const 
 {
 	if(mGLTexturep.notNull())
 	{
-		return mGLTexturep->getHasGLTexture() ;
+		return mGLTexturep->getHasGLTexture();
 	}
-	return FALSE ;
+	return FALSE;
 }
 
 BOOL LLGLTexture::getBoundRecently() const
 {
 	if(mGLTexturep.notNull())
 	{
-		return mGLTexturep->getBoundRecently() ;
+		return mGLTexturep->getBoundRecently();
 	}
-	return FALSE ;
+	return FALSE;
 }
 
 LLTexUnit::eTextureType LLGLTexture::getTarget(void) const
 {
-	llassert(mGLTexturep.notNull()) ;
-	return mGLTexturep->getTarget() ;
+	llassert(mGLTexturep.notNull());
+	return mGLTexturep->getTarget();
 }
 
 BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height)
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height) ;
+	return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height);
 }
 
 BOOL LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height)
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height) ;
+	return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height);
 }
 
 void LLGLTexture::setGLTextureCreated (bool initialized)
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	mGLTexturep->setGLTextureCreated (initialized) ;
+	mGLTexturep->setGLTextureCreated (initialized);
 }
 
 void  LLGLTexture::setCategory(S32 category) 
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
+
+	mGLTexturep->setCategory(category);
+}
 
-	mGLTexturep->setCategory(category) ;
+void LLGLTexture::setTexName(LLGLuint texName)
+{
+    llassert(mGLTexturep.notNull());
+    return mGLTexturep->setTexName(texName); 
+}
+
+void LLGLTexture::setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target)
+{
+    llassert(mGLTexturep.notNull());
+    return mGLTexturep->setTarget(target, bind_target); 
 }
 
 LLTexUnit::eTextureAddressMode LLGLTexture::getAddressMode(void) const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getAddressMode() ;
+	return mGLTexturep->getAddressMode();
 }
 
 S32Bytes LLGLTexture::getTextureMemory() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->mTextureMemory ;
+	return mGLTexturep->mTextureMemory;
 }
 
 LLGLenum LLGLTexture::getPrimaryFormat() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getPrimaryFormat() ;
+	return mGLTexturep->getPrimaryFormat();
 }
 
 BOOL LLGLTexture::getIsAlphaMask() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getIsAlphaMask() ;
+	return mGLTexturep->getIsAlphaMask();
 }
 
 BOOL LLGLTexture::getMask(const LLVector2 &tc)
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getMask(tc) ;
+	return mGLTexturep->getMask(tc);
 }
 
 F32 LLGLTexture::getTimePassedSinceLastBound()
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getTimePassedSinceLastBound() ;
+	return mGLTexturep->getTimePassedSinceLastBound();
 }
 BOOL LLGLTexture::getMissed() const 
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getMissed() ;
+	return mGLTexturep->getMissed();
 }
 
 BOOL LLGLTexture::isJustBound() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->isJustBound() ;
+	return mGLTexturep->isJustBound();
 }
 
 void LLGLTexture::forceUpdateBindStats(void) const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->forceUpdateBindStats() ;
+	return mGLTexturep->forceUpdateBindStats();
 }
 
 U32 LLGLTexture::getTexelsInAtlas() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getTexelsInAtlas() ;
+	return mGLTexturep->getTexelsInAtlas();
 }
 
 U32 LLGLTexture::getTexelsInGLTexture() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getTexelsInGLTexture() ;
+	return mGLTexturep->getTexelsInGLTexture();
 }
 
 BOOL LLGLTexture::isGLTextureCreated() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->isGLTextureCreated() ;
+	return mGLTexturep->isGLTextureCreated();
 }
 
 S32  LLGLTexture::getDiscardLevelInAtlas() const
 {
-	llassert(mGLTexturep.notNull()) ;
+	llassert(mGLTexturep.notNull());
 
-	return mGLTexturep->getDiscardLevelInAtlas() ;
+	return mGLTexturep->getDiscardLevelInAtlas();
 }
 
 void LLGLTexture::destroyGLTexture() 
 {
 	if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture())
 	{
-		mGLTexturep->destroyGLTexture() ;
-		mTextureState = DELETED ;
+		mGLTexturep->destroyGLTexture();
+		mTextureState = DELETED;
 	}
 }
 
 void LLGLTexture::setTexelsPerImage()
 {
-	S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT);
-	S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT);
-	mTexelsPerImage = (F32)fullwidth * fullheight;
+    U32 fullwidth = llmin(mFullWidth, (U32)MAX_IMAGE_SIZE_DEFAULT);
+    U32 fullheight = llmin(mFullHeight, (U32)MAX_IMAGE_SIZE_DEFAULT);
+	mTexelsPerImage = (U32)fullwidth * fullheight;
 }
 
+static LLUUID sStubUUID;
 
+const LLUUID& LLGLTexture::getID() const { return sStubUUID; }
\ No newline at end of file
diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h
index 45592ee077250ee959433fcdfc77dbc0cd90d750..a57e48ffad14fc0621c7a0aaf155fbf883527e0a 100644
--- a/indra/llrender/llgltexture.h
+++ b/indra/llrender/llgltexture.h
@@ -103,7 +103,7 @@ class LLGLTexture : public LLTexture
 
 	virtual void dump();	// debug info to LL_INFOS()
 
-	virtual const LLUUID& getID() const = 0;
+	virtual const LLUUID& getID() const;
 
 	void setBoostLevel(S32 level);
 	S32  getBoostLevel() { return mBoostLevel; }
@@ -132,6 +132,8 @@ class LLGLTexture : public LLTexture
 	BOOL       setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height);
 	void       setGLTextureCreated (bool initialized);
 	void       setCategory(S32 category) ;
+    void       setTexName(LLGLuint); // for forcing w/ externally created textures only
+    void setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target);
 
 	LLTexUnit::eTextureAddressMode getAddressMode(void) const ;
 	S32        getMaxDiscardLevel() const;
@@ -178,11 +180,11 @@ class LLGLTexture : public LLTexture
 
 protected:
 	S32 mBoostLevel;				// enum describing priority level
-	S32 mFullWidth;
-	S32 mFullHeight;
+	U32 mFullWidth;
+	U32 mFullHeight;
 	BOOL mUseMipMaps;
 	S8  mComponents;
-	F32 mTexelsPerImage;			// Texels per image.
+	U32 mTexelsPerImage;			// Texels per image.
 	mutable S8  mNeedsGLTexture;
 
 	//GL texture
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 20cba68f84779cbdbad1bc77560aa044f07662e6..75f6cd405aadc4232dc44ec2aa68f7a57e132e2d 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -352,10 +352,9 @@ BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, B
 }
 
 //----------------------------------------------------------------------------
-
 LLImageGL::LLImageGL(BOOL usemipmaps)
 :	LLTrace::MemTrackable<LLImageGL>("LLImageGL"),
-	mSaveData(0)
+    mSaveData(0), mExternalTexture(FALSE)
 {
 	init(usemipmaps);
 	setSize(0, 0, 0);
@@ -365,7 +364,7 @@ LLImageGL::LLImageGL(BOOL usemipmaps)
 
 LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps)
 :	LLTrace::MemTrackable<LLImageGL>("LLImageGL"),
-	mSaveData(0)
+    mSaveData(0), mExternalTexture(FALSE)
 {
 	llassert( components <= 4 );
 	init(usemipmaps);
@@ -376,7 +375,7 @@ LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps)
 
 LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps)
 :	LLTrace::MemTrackable<LLImageGL>("LLImageGL"),
-	mSaveData(0)
+    mSaveData(0), mExternalTexture(FALSE)
 {
 	init(usemipmaps);
 	setSize(0, 0, 0);
@@ -386,12 +385,36 @@ LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps)
 	createGLTexture(0, imageraw); 
 }
 
+LLImageGL::LLImageGL(
+    LLGLuint texName,
+    U32 components,
+    LLGLenum target,
+    LLGLint  formatInternal,
+    LLGLenum formatPrimary,
+    LLGLenum formatType,
+    LLTexUnit::eTextureAddressMode addressMode)
+    : LLTrace::MemTrackable<LLImageGL>("LLImageGL"), mSaveData(0), mExternalTexture(TRUE)
+{
+    init(false);
+    mTexName = texName;
+    mTarget = target;
+    mComponents = components;
+    mAddressMode = addressMode;
+    mFormatType = formatType;
+    mFormatInternal = formatInternal;
+    mFormatPrimary = formatPrimary;
+}
+
+
 LLImageGL::~LLImageGL()
 {
-	LLImageGL::cleanup();
-	sImageList.erase(this);
-	freePickMask();
-	sCount--;
+    if (!mExternalTexture)
+    {
+	    LLImageGL::cleanup();
+	    sImageList.erase(this);
+	    freePickMask();
+	    sCount--;
+    }
 }
 
 void LLImageGL::init(BOOL usemipmaps)
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index ad2aea90675e9b63d5f1bb6229e562fc44b1be68..bb0284a166ca451ea1e72edb2f3eccf3df524992 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -84,7 +84,10 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
 	LLImageGL(BOOL usemipmaps = TRUE);
 	LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE);
 	LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps = TRUE);
-	
+
+    // For wrapping textures created via GL elsewhere with our API only. Use with caution.
+    LLImageGL(LLGLuint mTexName, U32 components, LLGLenum target, LLGLint  formatInternal, LLGLenum formatPrimary, LLGLenum formatType, LLTexUnit::eTextureAddressMode addressMode);
+
 protected:
 	virtual ~LLImageGL();
 
@@ -234,6 +237,8 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
 	LLGLenum mFormatType;
 	BOOL	 mFormatSwapBytes;// if true, use glPixelStorei(GL_UNPACK_SWAP_BYTES, 1)
 	
+    BOOL mExternalTexture;
+
 	// STATICS
 public:	
 	static std::set<LLImageGL*> sImageList;
@@ -279,6 +284,8 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
 	void setCategory(S32 category) {mCategory = category;}
 	S32  getCategory()const {return mCategory;}
 	
+    void setTexName(GLuint texName) { mTexName = texName; }
+
 	//for debug use: show texture size distribution 
 	//----------------------------------------
 	static S32 sCurTexSizeBar ;
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 76f28bb43fcd9ddd812bfe5ab899d2dcc7bc896f..a8f622d3ff3ae29c92171fdfab223d4b87893d1c 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -49,6 +49,7 @@ U32 LLRender::sUICalls = 0;
 U32 LLRender::sUIVerts = 0;
 U32 LLTexUnit::sWhiteTexture = 0;
 bool LLRender::sGLCoreProfile = false;
+bool LLRender::sNsightDebugSupport = false;
 
 static const U32 LL_NUM_TEXTURE_LAYERS = 32; 
 static const U32 LL_NUM_LIGHT_UNITS = 8;
@@ -58,7 +59,8 @@ static const GLenum sGLTextureType[] =
 	GL_TEXTURE_2D,
 	GL_TEXTURE_RECTANGLE_ARB,
 	GL_TEXTURE_CUBE_MAP_ARB,
-	GL_TEXTURE_2D_MULTISAMPLE
+	GL_TEXTURE_2D_MULTISAMPLE,
+    GL_TEXTURE_3D
 };
 
 static const GLint sGLAddressMode[] =
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index a67fb8da522ff130259d44b2e02deb94c4f3eaa8..9ad3a6e5931e57842b3bf759355461516461499d 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -61,10 +61,11 @@ class LLTexUnit
 	typedef enum
 	{
 		TT_TEXTURE = 0,			// Standard 2D Texture
-		TT_RECT_TEXTURE,	// Non power of 2 texture
-		TT_CUBE_MAP,		// 6-sided cube map texture
+		TT_RECT_TEXTURE,	    // Non power of 2 texture
+		TT_CUBE_MAP,		    // 6-sided cube map texture
 		TT_MULTISAMPLE_TEXTURE, // see GL_ARB_texture_multisample
-		TT_NONE 		// No texture type is currently enabled
+        TT_TEXTURE_3D,          // standard 3D Texture
+		TT_NONE, 		        // No texture type is currently enabled        
 	} eTextureType;
 
 	typedef enum
@@ -438,7 +439,8 @@ class LLRender
 	static U32 sUICalls;
 	static U32 sUIVerts;
 	static bool sGLCoreProfile;
-	
+    static bool sNsightDebugSupport;
+
 private:
 	friend class LLLightState;
 
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index b010a4ae6465039b7f2d373d7cfbb3cecfee7522..287f22783f2c0bbae9b6c2e7e2d7e078a1143815 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -512,16 +512,16 @@ static std::string get_object_log(GLhandleARB ret)
 void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns, const std::string& filename) 
 {
 	std::string log = get_object_log(ret);
+    std::string fname = filename;
+    if (filename.empty())
+    {
+        fname = "unknown shader file";
+    }
 
-	if (log.length() > 0 || warns)
+	if (log.length() > 0)
 	{
-        LL_DEBUGS("ShaderLoading") << "Shader loading ";
-        
-		if (!filename.empty())
-		{
-            LL_CONT << "From " << filename << ":\n";
-		}
-        LL_CONT << log << LL_ENDL;
+        LL_INFOS() << "Shader loading from " << fname << ":\n" << LL_ENDL;
+        LL_INFOS() << log << LL_ENDL;
 	}
  }
 
@@ -1114,7 +1114,7 @@ void LLShaderMgr::initAttribsAndUniforms()
 	mReservedUniforms.push_back("specularMap");
 	mReservedUniforms.push_back("bumpMap");
 	mReservedUniforms.push_back("environmentMap");
-	mReservedUniforms.push_back("cloude_noise_texture");
+	mReservedUniforms.push_back("cloud_noise_texture");
 	mReservedUniforms.push_back("fullbright");
 	mReservedUniforms.push_back("lightnorm");
 	mReservedUniforms.push_back("sunlight_color");
@@ -1267,6 +1267,16 @@ void LLShaderMgr::initAttribsAndUniforms()
 
 	mReservedUniforms.push_back("origin");
 	mReservedUniforms.push_back("display_gamma");
+
+    mReservedUniforms.push_back("inscatter");
+    mReservedUniforms.push_back("sun_size");
+    mReservedUniforms.push_back("fog_color");
+
+    mReservedUniforms.push_back("transmittance_texture");
+    mReservedUniforms.push_back("scattering_texture");
+    mReservedUniforms.push_back("irradiance_texture");
+    mReservedUniforms.push_back("single_mie_scattering_texture");
+
 	llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS);
 
 	std::set<std::string> dupe_check;
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 7bdd97200d25b1609e58cccb4e958cd60e310961..ac3f7d58d5c0d524ae446e781d293c7048e9f67d 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -221,6 +221,17 @@ class LLShaderMgr
 		
 		SHINY_ORIGIN,
         DISPLAY_GAMMA,
+
+        INSCATTER_RT,
+        SUN_SIZE,
+        FOG_COLOR,
+
+        // precomputed textures from libatmosphere
+        TRANSMITTANCE_TEX,
+        SCATTER_TEX,
+        ILLUMINANCE_TEX,
+        SINGLE_MIE_SCATTER_TEX,
+
 		END_RESERVED_UNIFORMS
 	} eGLSLReservedUniforms;
 
diff --git a/indra/llrender/lltexture.cpp b/indra/llrender/lltexture.cpp
index 90fbcec2bedaad01b3ee84d258c13707fcb4021d..787a09a1594a6e10c99365d318baff6dc8a18c2c 100644
--- a/indra/llrender/lltexture.cpp
+++ b/indra/llrender/lltexture.cpp
@@ -29,3 +29,15 @@
 LLTexture::~LLTexture()
 {
 }
+
+S8   LLTexture::getType() const { llassert(false); return 0; }
+void LLTexture::setKnownDrawSize(S32 width, S32 height) { llassert(false); }
+bool LLTexture::bindDefaultImage(const S32 stage) { llassert(false); return false; }
+bool LLTexture::bindDebugImage(const S32 stage) { llassert(false); return false; }
+void LLTexture::forceImmediateUpdate() { llassert(false); }
+void LLTexture::setActive() { llassert(false);  }
+S32	 LLTexture::getWidth(S32 discard_level) const { llassert(false); return 0; }
+S32	 LLTexture::getHeight(S32 discard_level) const { llassert(false); return 0; }
+bool LLTexture::isActiveFetching() { llassert(false); return false; }
+LLImageGL* LLTexture::getGLTexture() const { llassert(false); return nullptr; }
+void LLTexture::updateBindStatsForTester() { }
\ No newline at end of file
diff --git a/indra/llrender/lltexture.h b/indra/llrender/lltexture.h
index 9fca8b8cd3a1544e2b05c9fd8c8a2282e082c1d6..41481fb8a722d23fd06e999ee9a9c7493ea02e59 100644
--- a/indra/llrender/lltexture.h
+++ b/indra/llrender/lltexture.h
@@ -58,21 +58,21 @@ class LLTexture : public virtual LLRefCount, public LLTrace::MemTrackable<LLText
 	//
 	//interfaces to access LLGLTexture
 	//
-	virtual S8         getType() const = 0 ;
-	virtual void       setKnownDrawSize(S32 width, S32 height) = 0 ;
-	virtual bool       bindDefaultImage(const S32 stage = 0) = 0 ;
-	virtual bool       bindDebugImage(const S32 stage = 0) = 0;
-	virtual void       forceImmediateUpdate() = 0 ;
-	virtual void       setActive() = 0 ;
-	virtual S32	       getWidth(S32 discard_level = -1) const = 0 ;
-	virtual S32	       getHeight(S32 discard_level = -1) const = 0 ;
-	virtual bool       isActiveFetching() = 0;
+	virtual S8         getType() const;
+	virtual void       setKnownDrawSize(S32 width, S32 height);
+	virtual bool       bindDefaultImage(const S32 stage = 0);
+	virtual bool       bindDebugImage(const S32 stage = 0);
+	virtual void       forceImmediateUpdate();
+	virtual void       setActive();
+	virtual S32	       getWidth(S32 discard_level = -1) const;
+	virtual S32	       getHeight(S32 discard_level = -1) const;
+	virtual bool       isActiveFetching();
 
 private:
 	//note: do not make this function public.
-	virtual LLImageGL* getGLTexture() const = 0 ;
+	virtual LLImageGL* getGLTexture() const;
 
-	virtual void updateBindStatsForTester() = 0 ;
+	virtual void updateBindStatsForTester();
 };
 #endif
 
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index ecd7c4bc3648c599466b2d87e3b4c468033072be..380d7762d6ac048b6e6232f44bad77708cd09f6d 100644
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -35,7 +35,7 @@
 						</array>
 					<key>tags</key>
 						<array>
-						</array>
+              </array>
 				</map>
 				<map>
 					<key>level</key><string>DEBUG</string>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 4a4f4bfc61946333a53ff25c2a8573d7bc5fdd01..d277a1158c0d91fcb887852e874624f1fdf0cd66 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10122,6 +10122,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+  <key>RenderUseAdvancedAtmospherics</key>
+  <map>
+    <key>Comment</key>
+    <string>Use fancy precomputed atmospherics and stuff.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>1</integer>
+  </map>
   <key>RenderUseTriStrips</key>
   <map>
     <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
index 03bdb754b57b22f057746cb8d5a5a7ef9ac9d8c8..7f1a8cce0d37c85d3a47c55e2be25a358fda9d22 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
@@ -397,7 +397,9 @@ void main()
 	float da = dot(norm.xyz, sun_dir.xyz);
 
 	float final_da = max(0.0,da);
-          final_da = min(final_da, 1.0f);
+              final_da = min(final_da, 1.0f);
+
+// why an ad hoc gamma boost here? srgb_to_linear instead?
 	      final_da = pow(final_da, 1.0/1.3);
 
 	vec4 diffuse = texture2DRect(diffuseRect, tc);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
index f7832521facdf2c56c437977789395ac9f1ed32b..cf076d156a4c9891f22adaf93e45819eea1506ed 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
@@ -276,10 +276,6 @@ void calcAtmospherics(vec3 inPositionEye, float ambFactor) {
 		  + tmpAmbient)));
 
 	//brightness of surface both sunlight and ambient
-	/*setSunlitColor(pow(vec3(sunlight * .5), vec3(global_gamma)) * global_gamma);
-	setAmblitColor(pow(vec3(tmpAmbient * .25), vec3(global_gamma)) * global_gamma);
-	setAdditiveColor(pow(getAdditiveColor() * vec3(1.0 - temp1), vec3(global_gamma)) * global_gamma);*/
-
 	setSunlitColor(vec3(sunlight * .5));
 	setAmblitColor(vec3(tmpAmbient * .25));
 	setAdditiveColor(getAdditiveColor() * vec3(1.0 - temp1));
diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl
index 81af1fdc8a217cc7902f16468f60599e2e9bf12a..b2d9de6e730ed65514d55f4db862eb4f7eac75c4 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl
@@ -238,8 +238,8 @@ void main()
 	
 	proj_tc.xyz /= proj_tc.w;
 	
-	float fa = falloff+1.0;
-	float dist_atten = min(1.0-(dist-1.0*(1.0-fa))/fa, 1.0);
+	float fa = falloff + 1.0;
+	float dist_atten = min(1.0 - (dist - 1.0 * (1.0 - fa)) / fa, 1.0);
 	dist_atten *= dist_atten;
 	dist_atten *= 2.0;
 
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 95e5cbe09e1080a63d9bb3e93001472e2f563918..85ec2a6ebc21121050604b20631afa5a4c709d92 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -205,6 +205,7 @@
 #include "llfloateroutfitsnapshot.h"
 #include "llfloatersnapshot.h"
 #include "llsidepanelinventory.h"
+#include "llatmosphere.h"
 
 // includes for idle() idleShutdown()
 #include "llviewercontrol.h"
@@ -1097,6 +1098,7 @@ bool LLAppViewer::init()
 		}
 	}
 
+#if LL_RELEASE_FOR_DOWNLOAD
 	char* PARENT = getenv("PARENT");
 	if (! (PARENT && std::string(PARENT) == "SL_Launcher"))
 	{
@@ -1109,6 +1111,7 @@ bool LLAppViewer::init()
 		// him/herself in the foot.
 		LLNotificationsUtil::add("RunLauncher");
 	}
+#endif
 
 #if LL_WINDOWS
 	if (gGLManager.mGLVersion < LLFeatureManager::getInstance()->getExpectedGLVersion())
@@ -1470,7 +1473,7 @@ bool LLAppViewer::frame()
 					LLAppViewer::getImageDecodeThread()->pause();
 				}
 			}
-			
+		
 			if (mRandomizeFramerate)
 			{
 				ms_sleep(rand() % 200);
@@ -1641,6 +1644,8 @@ void LLAppViewer::flushVFSIO()
 
 bool LLAppViewer::cleanup()
 {
+    LLAtmosphere::cleanupClass();
+
 	//ditch LLVOAvatarSelf instance
 	gAgentAvatarp = NULL;
 
diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp
index f10c116555e64b7a66af2094b9d066a6b764f4f4..042d7d971a5fb5cd40c33fb083c43ce82484fc55 100644
--- a/indra/newview/lldrawpoolwlsky.cpp
+++ b/indra/newview/lldrawpoolwlsky.cpp
@@ -42,6 +42,7 @@
 #include "llrender.h"
 
 #include "llenvironment.h" 
+#include "llatmosphere.h"
 
 LLPointer<LLViewerTexture> LLDrawPoolWLSky::sCloudNoiseTexture = NULL;
 
@@ -125,38 +126,51 @@ void LLDrawPoolWLSky::endDeferredPass(S32 pass)
 
 void LLDrawPoolWLSky::renderDome(F32 camHeightLocal, LLGLSLShader * shader) const
 {
-	LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
-
-	llassert_always(NULL != shader);
-
-	gGL.pushMatrix();
-
-	//chop off translation
-	if (LLPipeline::sReflectionRender && origin.mV[2] > 256.f)
-	{
-		gGL.translatef(origin.mV[0], origin.mV[1], 256.f-origin.mV[2]*0.5f);
-	}
-	else
-	{
-		gGL.translatef(origin.mV[0], origin.mV[1], origin.mV[2]);
-	}
+    llassert_always(NULL != shader);
+
+    static LLStaticHashedString sCamPosLocal("camPosLocal");
+
+    LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
+
+    if (gPipeline.useAdvancedAtmospherics())
+    {
+        // Draw WL Sky	w/ normal cam pos (where you are) for adv atmo sky
+        sky_shader->uniform3f(sCamPosLocal, origin.mV[0], origin.mV[1], origin.mV[2]);
+
+//  TBD replace this with a FS tri pass, there's little point to the tess when you have fragment shaders...
+
+        gSky.mVOWLSkyp->drawDome();
+    }
+    else
+    {
+	    gGL.pushMatrix();
+
+	    //chop off translation
+	    if (LLPipeline::sReflectionRender && origin.mV[2] > 256.f)
+	    {
+		    gGL.translatef(origin.mV[0], origin.mV[1], 256.f-origin.mV[2]*0.5f);
+	    }
+	    else
+	    {
+		    gGL.translatef(origin.mV[0], origin.mV[1], origin.mV[2]);
+	    }
 		
 
-	// the windlight sky dome works most conveniently in a coordinate system
-	// where Y is up, so permute our basis vectors accordingly.
-	gGL.rotatef(120.f, 1.f / F_SQRT3, 1.f / F_SQRT3, 1.f / F_SQRT3);
+	    // the windlight sky dome works most conveniently in a coordinate system
+	    // where Y is up, so permute our basis vectors accordingly.
+	    gGL.rotatef(120.f, 1.f / F_SQRT3, 1.f / F_SQRT3, 1.f / F_SQRT3);
 
-	gGL.scalef(0.333f, 0.333f, 0.333f);
+	    gGL.scalef(0.333f, 0.333f, 0.333f);
 
-	gGL.translatef(0.f,-camHeightLocal, 0.f);
+	    gGL.translatef(0.f,-camHeightLocal, 0.f);
 	
-	// Draw WL Sky	
-	static LLStaticHashedString sCamPosLocal("camPosLocal");
-	shader->uniform3f(sCamPosLocal, 0.f, camHeightLocal, 0.f);
+	    // Draw WL Sky
+	    shader->uniform3f(sCamPosLocal, 0.f, camHeightLocal, 0.f);
 
-	gSky.mVOWLSkyp->drawDome();
+        gSky.mVOWLSkyp->drawDome();
 
-	gGL.popMatrix();
+	    gGL.popMatrix();
+    }
 }
 
 void LLDrawPoolWLSky::renderSkyHaze(F32 camHeightLocal) const
@@ -167,6 +181,33 @@ void LLDrawPoolWLSky::renderSkyHaze(F32 camHeightLocal) const
 
 		sky_shader->bind();
 
+        if (gPipeline.useAdvancedAtmospherics() && gPipeline.canUseWindLightShaders() && gAtmosphere)
+        {
+            // bind precomputed textures necessary for calculating sun and sky luminance
+            sky_shader->bindTexture(LLShaderMgr::TRANSMITTANCE_TEX, gAtmosphere->getTransmittance());
+            sky_shader->bindTexture(LLShaderMgr::SCATTER_TEX, gAtmosphere->getScattering());
+            sky_shader->bindTexture(LLShaderMgr::SINGLE_MIE_SCATTER_TEX, gAtmosphere->getSingleMieScattering());
+
+            static float sunSize = (float)cos(0.0005);
+
+            sky_shader->uniform1f(LLShaderMgr::SUN_SIZE, sunSize);
+
+            static LLVector3 solDir(0.7f, 0.2f, 0.2f);
+
+            //neither of these appear to track with the env settings, would the real sun please stand up.
+            //sky_shader->uniform3fv(LLShaderMgr::DEFERRED_SUN_DIR, 1, gPipeline.mTransformedSunDir.mV);
+            //sky_shader->uniform3fv(LLShaderMgr::DEFERRED_SUN_DIR, 1, gSky.mVOSkyp->getSun().getDirection().mV);
+            solDir.normalize();
+
+            sky_shader->uniform3fv(LLShaderMgr::DEFERRED_SUN_DIR, 1, solDir.mV);
+
+            // clouds are rendered along with sky in adv atmo
+            if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS) && sCloudNoiseTexture.notNull())
+            {
+                sky_shader->bindTexture(LLShaderMgr::CLOUD_NOISE_MAP, sCloudNoiseTexture);
+            }
+        }
+
 		/// Render the skydome
 		renderDome(camHeightLocal, sky_shader);	
 
@@ -190,8 +231,9 @@ void LLDrawPoolWLSky::renderStars(void) const
 	// *NOTE: we divide by two here and GL_ALPHA_SCALE by two below to avoid
 	// clamping and allow the star_alpha param to brighten the stars.
 	LLColor4 star_alpha(LLColor4::black);
+
     // *LAPRAS
-    star_alpha.mV[3] = LLEnvironment::instance().getCurrentSky()->getStarBrightness() / 2.f;
+    star_alpha.mV[3] = LLEnvironment::instance().getCurrentSky()->getStarBrightness() / (2.f + ((rand() >> 16)/65535.0f)); // twinkle twinkle
 
 	// If start_brightness is not set, exit
 	if( star_alpha.mV[3] < 0.001 )
@@ -322,8 +364,10 @@ void LLDrawPoolWLSky::renderDeferred(S32 pass)
 
 	renderSkyHaze(camHeightLocal);
 
-	LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
-	gGL.pushMatrix();
+    if (!gPipeline.useAdvancedAtmospherics() && gPipeline.canUseWindLightShaders())
+    {
+	    LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
+	    gGL.pushMatrix();
 
 		
 		gGL.translatef(origin.mV[0], origin.mV[1], origin.mV[2]);
@@ -340,13 +384,12 @@ void LLDrawPoolWLSky::renderDeferred(S32 pass)
 		
 		gDeferredStarProgram.unbind();
 
-	gGL.popMatrix();
+	    gGL.popMatrix();
+    }
 
 	renderSkyClouds(camHeightLocal);
-
-	gGL.setColorMask(true, true);
-	//gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
+    
+    gGL.setColorMask(true, true);
 }
 
 void LLDrawPoolWLSky::render(S32 pass)
@@ -367,8 +410,10 @@ void LLDrawPoolWLSky::render(S32 pass)
 
 	renderSkyHaze(camHeightLocal);
 
-	LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
-	gGL.pushMatrix();
+    if (!gPipeline.useAdvancedAtmospherics() && gPipeline.canUseWindLightShaders())
+    {
+	    LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
+	    gGL.pushMatrix();
 
 		gGL.translatef(origin.mV[0], origin.mV[1], origin.mV[2]);
 
@@ -380,9 +425,9 @@ void LLDrawPoolWLSky::render(S32 pass)
 		renderHeavenlyBodies();
 
 		renderStars();
-		
 
-	gGL.popMatrix();
+	    gGL.popMatrix();
+    }
 
 	renderSkyClouds(camHeightLocal);
 
diff --git a/indra/newview/lldrawpoolwlsky.h b/indra/newview/lldrawpoolwlsky.h
index cd15c991ee091fefb8e4f3f6a7ab452fa77364bb..586219e4bc43d2a66c7821eccc285d83372e29dd 100644
--- a/indra/newview/lldrawpoolwlsky.h
+++ b/indra/newview/lldrawpoolwlsky.h
@@ -39,6 +39,8 @@ class LLDrawPoolWLSky : public LLDrawPool {
 	static const U32 STAR_VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
 		LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0;
 
+    static const U32 ADV_ATMO_SKY_VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX;
+
 	LLDrawPoolWLSky(void);
 	/*virtual*/ ~LLDrawPoolWLSky();
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index df708013fc8aae3d5264afa293497497310df2e3..6e89763ba2927851cfc6fc64227b3c23c807245a 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2507,7 +2507,7 @@ void LLMeshUploadThread::requestWholeModelFee()
 		{
 			ms_sleep(sleep_time);
 			sleep_time = llmin(250U, sleep_time + sleep_time);
-			mHttpRequest->update(0);
+		    mHttpRequest->update(0);
 		}
 		if (isDiscarded())
 		{
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index e40d3da33883f546ed56f844d48bd6e7c95aee8d..bec05fa26300bbbe5ac129efe18d02682118a780 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -44,6 +44,7 @@
 #include "lljoint.h"
 #include "llskinningutil.h"
 #include "llenvironment.h"
+#include "llatmosphere.h"
 
 #ifdef LL_RELEASE_FOR_DOWNLOAD
 #define UNIFORM_ERRS LL_WARNS_ONCE("Shader")
@@ -92,6 +93,8 @@ LLGLSLShader	gDebugProgram;
 LLGLSLShader	gClipProgram;
 LLGLSLShader	gDownsampleDepthProgram;
 LLGLSLShader	gDownsampleDepthRectProgram;
+LLGLSLShader	gDownsampleMinMaxDepthRectProgram;
+LLGLSLShader	gInscatterRectProgram;
 LLGLSLShader	gAlphaMaskProgram;
 LLGLSLShader	gBenchmarkProgram;
 
@@ -474,7 +477,7 @@ void LLViewerShaderMgr::setShaders()
 		S32 env_class = 2;
 		S32 obj_class = 2;
 		S32 effect_class = 2;
-		S32 wl_class = 2;
+		S32 wl_class = 3;
 		S32 water_class = 2;
 		S32 deferred_class = 0;
 		S32 transform_class = gGLManager.mHasTransformFeedback ? 1 : 0;
@@ -498,14 +501,13 @@ void LLViewerShaderMgr::setShaders()
 			{ //no shadows
 				deferred_class = 1;
 			}
-
-			//make sure hardware skinning is enabled
-			//gSavedSettings.setBOOL("RenderAvatarVP", TRUE);
-			
-			//make sure atmospheric shaders are enabled
-			//gSavedSettings.setBOOL("WindLightUseAtmosShaders", TRUE);
 		}
 
+        // clamp to WL class 2 if we have disabled adv atmo (class 3)
+        if (!gSavedSettings.getBOOL("RenderUseAdvancedAtmospherics"))
+        {
+            wl_class = llmin(wl_class, 2);
+        }
 
 		if (!(LLFeatureManager::getInstance()->isFeatureAvailable("WindLightUseAtmosShaders")
 			  && gSavedSettings.getBOOL("WindLightUseAtmosShaders")))
@@ -515,7 +517,6 @@ void LLViewerShaderMgr::setShaders()
 			wl_class = 1;
 		}
 
-		
 		// Trigger a full rebuild of the fallback skybox / cubemap if we've toggled windlight shaders
 		if (mVertexShaderLevel[SHADER_WINDLIGHT] != wl_class && gSky.mVOSkyp.notNull())
 		{
@@ -543,6 +544,7 @@ void LLViewerShaderMgr::setShaders()
 
 			// Load all shaders to set max levels
 			loaded = loadShadersEnvironment();
+			llassert(loaded);
 
 			if (loaded)
 			{
@@ -579,14 +581,10 @@ void LLViewerShaderMgr::setShaders()
 				if (gSavedSettings.getBOOL("RenderAvatarVP") && loadShadersObject())
 				{ //hardware skinning is enabled and rigged attachment shaders loaded correctly
 					BOOL avatar_cloth = gSavedSettings.getBOOL("RenderAvatarCloth");
-					S32 avatar_class = 1;
-				
-					// cloth is a class3 shader
-					if(avatar_cloth)
-					{
-						avatar_class = 3;
-					}
 
+					// cloth is a class3 shader
+					S32 avatar_class = avatar_cloth ? 3 : 1;
+				
 					// Set the actual level
 					mVertexShaderLevel[SHADER_AVATAR] = avatar_class;
 					loadShadersAvatar();
@@ -699,6 +697,8 @@ void LLViewerShaderMgr::unloadShaders()
 	gClipProgram.unload();
 	gDownsampleDepthProgram.unload();
 	gDownsampleDepthRectProgram.unload();
+	gDownsampleMinMaxDepthRectProgram.unload();
+    gInscatterRectProgram.unload();
 	gBenchmarkProgram.unload();
 	gAlphaMaskProgram.unload();
 	gUIProgram.unload();
@@ -1954,15 +1954,19 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 	{
 		gDeferredWLSkyProgram.mName = "Deferred Windlight Sky Shader";
 		//gWLSkyProgram.mFeatures.hasGamma = true;
-		gDeferredWLSkyProgram.mShaderFiles.clear();
+        gDeferredWLSkyProgram.mShaderFiles.clear();
 		gDeferredWLSkyProgram.mShaderFiles.push_back(make_pair("deferred/skyV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredWLSkyProgram.mShaderFiles.push_back(make_pair("deferred/skyF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDeferredWLSkyProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+        gDeferredWLSkyProgram.mShaderLevel = mVertexShaderLevel[SHADER_WINDLIGHT];
 		gDeferredWLSkyProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+        if (mVertexShaderLevel[SHADER_WINDLIGHT] >= 3)
+        {
+            gDeferredWLSkyProgram.mExtraLinkObject = gAtmosphere->getAtmosphericShaderForLink();
+        }
 		success = gDeferredWLSkyProgram.createShader(NULL, NULL);
 	}
 
-	if (success)
+    if (success && (mVertexShaderLevel[SHADER_WINDLIGHT] < 3))
 	{
 		gDeferredWLCloudProgram.mName = "Deferred Windlight Cloud Program";
 		gDeferredWLCloudProgram.mShaderFiles.clear();
@@ -3265,16 +3269,6 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
 		success = gDownsampleDepthRectProgram.createShader(NULL, NULL);
 	}
 
-	if (success)
-	{
-		gDownsampleDepthRectProgram.mName = "DownsampleDepthRect Shader";
-		gDownsampleDepthRectProgram.mShaderFiles.clear();
-		gDownsampleDepthRectProgram.mShaderFiles.push_back(make_pair("interface/downsampleDepthV.glsl", GL_VERTEX_SHADER_ARB));
-		gDownsampleDepthRectProgram.mShaderFiles.push_back(make_pair("interface/downsampleDepthRectF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDownsampleDepthRectProgram.mShaderLevel = mVertexShaderLevel[SHADER_INTERFACE];
-		success = gDownsampleDepthRectProgram.createShader(NULL, NULL);
-	}
-
 	if (success)
 	{
 		gAlphaMaskProgram.mName = "Alpha Mask Shader";
@@ -3302,9 +3296,43 @@ BOOL LLViewerShaderMgr::loadShadersWindLight()
 	{
 		gWLSkyProgram.unload();
 		gWLCloudProgram.unload();
+		gDownsampleMinMaxDepthRectProgram.unload();
+        gInscatterRectProgram.unload();
 		return TRUE;
 	}
 
+    if (mVertexShaderLevel[SHADER_WINDLIGHT] >= 3)
+    {
+        // Prepare precomputed atmospherics textures using libatmosphere
+        LLAtmosphere::initClass();
+    }
+
+	// this shader uses gather so it can't live with the other basic shaders safely
+	if (success)
+	{
+		gDownsampleMinMaxDepthRectProgram.mName = "DownsampleMinMaxDepthRect Shader";
+		gDownsampleMinMaxDepthRectProgram.mShaderFiles.clear();
+		gDownsampleMinMaxDepthRectProgram.mShaderFiles.push_back(make_pair("windlight/downsampleMinMaxDepthV.glsl", GL_VERTEX_SHADER_ARB));
+		gDownsampleMinMaxDepthRectProgram.mShaderFiles.push_back(make_pair("windlight/downsampleMinMaxDepthRectF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDownsampleMinMaxDepthRectProgram.mShaderLevel = mVertexShaderLevel[SHADER_WINDLIGHT];
+		success = gDownsampleMinMaxDepthRectProgram.createShader(NULL, NULL);
+	}
+
+    // this shader uses gather so it can't live with the other basic shaders safely
+    if (success && (mVertexShaderLevel[SHADER_WINDLIGHT] >= 3))
+    {
+        gInscatterRectProgram.mName = "Inscatter Shader";
+        gInscatterRectProgram.mShaderFiles.clear();
+        gInscatterRectProgram.mShaderFiles.push_back(make_pair("windlight/atmoV.glsl", GL_VERTEX_SHADER_ARB));
+        gInscatterRectProgram.mShaderFiles.push_back(make_pair("windlight/atmoF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gInscatterRectProgram.mShaderLevel = mVertexShaderLevel[SHADER_WINDLIGHT];       
+        llassert(gAtmosphere != nullptr);
+        gInscatterRectProgram.mExtraLinkObject = gAtmosphere->getAtmosphericShaderForLink();
+        success = gInscatterRectProgram.createShader(NULL, NULL);
+    }
+
+    llassert(success);
+
 	if (success)
 	{
 		gWLSkyProgram.mName = "Windlight Sky Shader";
@@ -3314,10 +3342,16 @@ BOOL LLViewerShaderMgr::loadShadersWindLight()
 		gWLSkyProgram.mShaderFiles.push_back(make_pair("windlight/skyF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gWLSkyProgram.mShaderLevel = mVertexShaderLevel[SHADER_WINDLIGHT];
 		gWLSkyProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+        if (mVertexShaderLevel[SHADER_WINDLIGHT] >= 3)
+        {
+            gWLSkyProgram.mExtraLinkObject = gAtmosphere->getAtmosphericShaderForLink();
+        }
 		success = gWLSkyProgram.createShader(NULL, NULL);
 	}
 
-	if (success)
+    llassert(success);
+
+    if (success && (mVertexShaderLevel[SHADER_WINDLIGHT] < 3))
 	{
 		gWLCloudProgram.mName = "Windlight Cloud Program";
 		//gWLCloudProgram.mFeatures.hasGamma = true;
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 923aa522ad2ebe85e74bb108eae0c6bafd577a5e..10c60187f31b585abcef31c7ccb2d2005826a0c5 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -184,6 +184,8 @@ extern LLGLSLShader			gDebugProgram;
 extern LLGLSLShader			gClipProgram;
 extern LLGLSLShader			gDownsampleDepthProgram;
 extern LLGLSLShader			gDownsampleDepthRectProgram;
+extern LLGLSLShader			gDownsampleMinMaxDepthRectProgram;
+extern LLGLSLShader			gInscatterRectProgram;
 extern LLGLSLShader			gBenchmarkProgram;
 
 //output tex0[tc0] + tex1[tc1]
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index e5a1bed48cbc06cda219213d65d26792c62a4e33..cbde0af9b3dc22720adc824936c62bbf2294fef2 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1176,12 +1176,12 @@ void LLViewerFetchedTexture::loadFromFastCache()
 	{
 		return; //no need to access the fast cache.
 	}
-	mInFastCacheList = FALSE;
+    mInFastCacheList = FALSE;
 
 	mRawImage = LLAppViewer::getTextureCache()->readFromFastCache(getID(), mRawDiscardLevel);
 	if(mRawImage.notNull())
 	{
-		mFullWidth = mRawImage->getWidth() << mRawDiscardLevel;
+		mFullWidth  = mRawImage->getWidth()  << mRawDiscardLevel;
 		mFullHeight = mRawImage->getHeight() << mRawDiscardLevel;
 		setTexelsPerImage();
 
@@ -1196,20 +1196,20 @@ void LLViewerFetchedTexture::loadFromFastCache()
 		else
 		{
             if (mBoostLevel == LLGLTexture::BOOST_ICON)
+        {
+            S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS;
+            S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS;
+            if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
             {
-                S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS;
-                S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS;
-                if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
-                {
-                    // scale oversized icon, no need to give more work to gl
-                    mRawImage->scale(expected_width, expected_height);
-                }
+                // scale oversized icon, no need to give more work to gl
+                mRawImage->scale(expected_width, expected_height);
+            }
             }
 
-			mRequestedDiscardLevel = mDesiredDiscardLevel + 1;
-			mIsRawImageValid = TRUE;			
-			addToCreateTexture();
-		}
+		mRequestedDiscardLevel = mDesiredDiscardLevel + 1;
+		mIsRawImageValid = TRUE;			
+		addToCreateTexture();
+	}
 	}
 }
 
@@ -1965,7 +1965,7 @@ bool LLViewerFetchedTexture::updateFetch()
 				mIsFetched = TRUE;
 				tester->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID));
 			}
-			mRawDiscardLevel = fetch_discard;
+            mRawDiscardLevel = fetch_discard;
 			if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) &&
 				(current_discard < 0 || mRawDiscardLevel < current_discard))
 			{
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index c9dea17f63b77fa03036f3d450f0240f423d35fe..5bc274ee5be733980b4410f41bcead04e697e76c 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -225,8 +225,8 @@ class LLViewerTexture : public LLGLTexture
 	static S8  sCameraMovingDiscardBias;
 	static F32 sCameraMovingBias;
 	static S32 sMaxSculptRez ;
-	static S32 sMinLargeImageSize ;
-	static S32 sMaxSmallImageSize ;
+	static U32 sMinLargeImageSize ;
+	static U32 sMaxSmallImageSize ;
 	static BOOL sFreezeImageScalingDown ;//do not scale down image res if set.
 	static F32  sCurrentTime ;
 
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 0ac1bfa3e76672581a1cc0a01b0d280d6cfb1478..849273df1508c1252cece6b4838f24f1c3bf1ca4 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -381,6 +381,7 @@ bool	LLPipeline::sRenderBump = true;
 bool	LLPipeline::sBakeSunlight = false;
 bool	LLPipeline::sNoAlpha = false;
 bool	LLPipeline::sUseTriStrips = true;
+bool	LLPipeline::sUseAdvancedAtmospherics = true;
 bool	LLPipeline::sUseFarClip = true;
 bool	LLPipeline::sShadowRender = false;
 bool	LLPipeline::sWaterReflections = false;
@@ -480,6 +481,7 @@ void LLPipeline::init()
 	sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD");
 	sRenderBump = gSavedSettings.getBOOL("RenderObjectBump");
 	sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips");
+	sUseAdvancedAtmospherics = gSavedSettings.getBOOL("RenderUseAdvancedAtmospherics");
 	LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO");
 	LLVertexBuffer::sUseVAO = gSavedSettings.getBOOL("RenderUseVAO");
 	LLVertexBuffer::sPreferStreamDraw = gSavedSettings.getBOOL("RenderPreferStreamDraw");
@@ -981,7 +983,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
 			for (U32 i = 0; i < 4; i++)
 			{
 				if (!mShadow[i].allocate(sun_shadow_map_width,U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE)) return false;
-				if (!mShadowOcclusion[i].allocate(mShadow[i].getWidth()/occlusion_divisor, mShadow[i].getHeight()/occlusion_divisor, 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE)) return false;
+				if (!mShadowOcclusion[i].allocate(mShadow[i].getWidth()/occlusion_divisor, mShadow[i].getHeight()/occlusion_divisor, 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE)) return false;                
 			}
 		}
 		else
@@ -993,6 +995,13 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
 			}
 		}
 
+// for EEP atmospherics
+		bool allocated_inscatter = mInscatter.allocate(resX >> 2, resY >> 2, GL_RGBA16F_ARB, FALSE, FALSE, LLTexUnit::TT_TEXTURE);
+        	if (!allocated_inscatter)
+        	{
+        	    return false;
+        	}
+
 		U32 width = (U32) (resX*scale);
 		U32 height = width;
 
@@ -1229,6 +1238,8 @@ void LLPipeline::releaseScreenBuffers()
 		mShadow[i].release();
 		mShadowOcclusion[i].release();
 	}
+
+	mInscatter.release();
 }
 
 
@@ -2646,6 +2657,65 @@ void LLPipeline::markOccluder(LLSpatialGroup* group)
 	}
 }
 
+void LLPipeline::downsampleMinMaxDepthBuffer(LLRenderTarget& source, LLRenderTarget& dest, LLRenderTarget* scratch_space)
+{
+	LLGLSLShader* last_shader = LLGLSLShader::sCurBoundShaderPtr;
+
+	LLGLSLShader* shader = NULL;
+
+	if (scratch_space)
+	{
+		scratch_space->copyContents(source,
+			0, 0, source.getWidth(), source.getHeight(),
+			0, 0, scratch_space->getWidth(), scratch_space->getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+	}
+
+	dest.bindTarget();
+	dest.clear(GL_COLOR_BUFFER_BIT); // dest should be an RG16F target
+
+	LLStrider<LLVector3> vert;
+	mDeferredVB->getVertexStrider(vert);
+	LLStrider<LLVector2> tc0;
+
+	vert[0].set(-1, 1, 0);
+	vert[1].set(-1, -3, 0);
+	vert[2].set(3, 1, 0);
+
+	if (source.getUsage() == LLTexUnit::TT_RECT_TEXTURE)
+	{
+		shader = &gDownsampleMinMaxDepthRectProgram;
+		shader->bind();
+		shader->uniform2f(sDelta, 1.f, 1.f);
+		shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, source.getWidth(), source.getHeight());
+	}
+	else
+	{
+		shader = &gDownsampleMinMaxDepthRectProgram;
+		shader->bind();
+		shader->uniform2f(sDelta, 1.f / source.getWidth(), 1.f / source.getHeight());
+		shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, 1.f, 1.f);
+	}
+
+	gGL.getTexUnit(0)->bind(scratch_space ? scratch_space : &source, TRUE);
+
+	{
+		LLGLDepthTest depth(GL_FALSE, GL_FALSE, GL_ALWAYS);
+		mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX);
+		mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+	}
+
+	dest.flush();
+
+	if (last_shader)
+	{
+		last_shader->bind();
+	}
+	else
+	{
+		shader->unbind();
+	}
+}
+
 void LLPipeline::downsampleDepthBuffer(LLRenderTarget& source, LLRenderTarget& dest, LLRenderTarget* scratch_space)
 {
 	LLGLSLShader* last_shader = LLGLSLShader::sCurBoundShaderPtr;
@@ -8282,6 +8352,21 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n
 		}
 	}
 
+    channel = shader.enableTexture(LLShaderMgr::INSCATTER_RT, LLTexUnit::TT_TEXTURE);
+    stop_glerror();
+    if (channel > -1)
+    {
+        stop_glerror();
+        gGL.getTexUnit(channel)->bind(&mInscatter, TRUE);
+        gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
+        gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+        stop_glerror();
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_ALWAYS);
+        stop_glerror();
+    }
+
 	stop_glerror();
 
 	F32 mat[16*6];
@@ -9095,6 +9180,7 @@ void LLPipeline::renderDeferredLightingToRT(LLRenderTarget* target)
 					}
 				}
 
+// pretty sure this doesn't work as expected since the shaders using 'shadow_ofset' all declare it as a single uniform float, no array or vec
 				gDeferredSunProgram.uniform3fv(LLShaderMgr::DEFERRED_SHADOW_OFFSET, slice, offset);
 				gDeferredSunProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, mDeferredLight.getWidth(), mDeferredLight.getHeight());
 				
@@ -9911,9 +9997,9 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 						}
 						else
 						{
-						renderGeom(camera);
-					}
-				}	
+						    renderGeom(camera);
+					    }
+				    }	
 				}	
 
 				if (LLPipeline::sRenderDeferred && materials_in_water)
@@ -10412,23 +10498,25 @@ bool LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 			
 		for (U32 j = 0; j < 3; ++j)
 		{
-			if (p[j] < ext[0].mV[j] ||
-				p[j] > ext[1].mV[j])
+			if (p[j] < ext[0].mV[j] || p[j] > ext[1].mV[j])
 			{
 				found = false;
 				break;
 			}
 		}
-				
-		for (U32 j = 0; j < LLCamera::AGENT_PLANE_NO_USER_CLIP_NUM; ++j)
+		
+		if (found) // don't bother testing user clip planes if we're already rejected...
 		{
-			const LLPlane& cp = camera.getAgentPlane(j);
-			F32 dist = cp.dist(pp[i]);
-			if (dist > 0.05f) //point is above some plane, not contained
-			{
-				found = false;
-				break;
-			}
+		    for (U32 j = 0; j < LLCamera::AGENT_PLANE_NO_USER_CLIP_NUM; ++j)
+		    {
+			    const LLPlane& cp = camera.getAgentPlane(j);
+			    F32 dist = cp.dist(pp[i]);
+			    if (dist > 0.05f) //point is above some plane, not contained
+			    {
+				    found = false;
+				    break;
+			    }
+		    }
 		}
 
 		if (found)
@@ -11959,3 +12047,7 @@ void LLPipeline::restoreHiddenObject( const LLUUID& id )
 	}
 }
 
+bool LLPipeline::useAdvancedAtmospherics() const
+{
+    return sUseAdvancedAtmospherics;
+}
\ No newline at end of file
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index c9670a60f26cdcf265e3e5d810ceb02ffe2cf5d9..6023a41ca285dd9a7bd1ba2b3779dfbfed5a62aa 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -168,6 +168,9 @@ class LLPipeline
 	// if source's depth buffer cannot be bound for reading, a scratch space depth buffer must be provided
 	void		downsampleDepthBuffer(LLRenderTarget& source, LLRenderTarget& dest, LLRenderTarget* scratch_space = NULL);
 
+	// Downsample depth buffer with gather and find local min/max depth values. Writes to a 16F RG render target.
+	void		downsampleMinMaxDepthBuffer(LLRenderTarget& source, LLRenderTarget& dest, LLRenderTarget* scratch_space = NULL);
+
 	void		doOcclusion(LLCamera& camera, LLRenderTarget& source, LLRenderTarget& dest, LLRenderTarget* scratch_space = NULL);
 	void		doOcclusion(LLCamera& camera);
 	void		markNotCulled(LLSpatialGroup* group, LLCamera &camera);
@@ -541,6 +544,8 @@ class LLPipeline
 
 	void updateCamera(bool reset = false);
 	
+	bool useAdvancedAtmospherics() const;
+
 	LLVector3				mFlyCamPosition;
 	LLQuaternion			mFlyCamRotation;
 
@@ -568,6 +573,7 @@ class LLPipeline
 	static bool				sBakeSunlight;
 	static bool				sNoAlpha;
 	static bool				sUseTriStrips;
+	static bool				sUseAdvancedAtmospherics;
 	static bool				sUseFarClip;
 	static bool				sShadowRender;
 	static bool				sWaterReflections;
@@ -614,12 +620,13 @@ class LLPipeline
 	//sun shadow map
 	LLRenderTarget			mShadow[6];
 	LLRenderTarget			mShadowOcclusion[6];
-	std::vector<LLVector3>	mShadowFrustPoints[4];
-	LLVector4				mShadowError;
-	LLVector4				mShadowFOV;
-	LLVector3				mShadowFrustOrigin[4];
-	LLCamera				mShadowCamera[8];
-	LLVector3				mShadowExtents[4][2];
+	LLRenderTarget			mInscatter;
+	std::vector<LLVector3>		mShadowFrustPoints[4];
+	LLVector4			mShadowError;
+	LLVector4			mShadowFOV;
+	LLVector3			mShadowFrustOrigin[4];
+	LLCamera			mShadowCamera[8];
+	LLVector3			mShadowExtents[4][2];
 	glh::matrix4f			mSunShadowMatrix[6];
 	glh::matrix4f			mShadowModelview[6];
 	glh::matrix4f			mShadowProjection[6];