diff --git a/.hgignore b/.hgignore
index 51b864651b26f47a11bf81095ad9d903a914c8a0..bd4f19a91e985dd5916c6c8b5a745c581cf32093 100644
--- a/.hgignore
+++ b/.hgignore
@@ -9,6 +9,7 @@ syntax: glob
 .*.swp
 #OSX image cache file
 *.DS_Store
+*.orig
 LICENSES
 indra/.distcc
 build-linux-*
diff --git a/.hgtags b/.hgtags
index a0cb87d2d81499858dbfa5db27830f31dad8450b..f32e71f85c18d79eb615a75554124fb4a32fb74e 100644
--- a/.hgtags
+++ b/.hgtags
@@ -8,6 +8,7 @@ bb38ff1a763738609e1b3cada6d15fa61e5e84b9 2.1.1-release
 1e2b517adc2ecb342cd3c865f2a6ccf82a3cf8d7 2-1-beta-3
 3469d90a115b900f8f250e137bbd9b684130f5d2 beta-4
 3e4b947f79d88c385e8218cbc0731cef0e42cfc4 2-1-beta-1
+434973a76ab2755f98ab55e1afc193e16692d5c5 2-1-1-beta-2
 46002088d9a4489e323b8d56131c680eaa21258c viewer-2-1-0-start
 4f777ffb99fefdc6497c61385c22688ff149c659 viewer-2-0-0
 52d96ad3d39be29147c5b2181b3bb46af6164f0e alpha-3
@@ -16,6 +17,7 @@ bb38ff1a763738609e1b3cada6d15fa61e5e84b9 2.1.1-release
 7f16e79826d377f5f9f5b33dc721ab56d0d7dc8f alpha-4
 7f16e79826d377f5f9f5b33dc721ab56d0d7dc8f fork to viewer-20qa
 80bc6cff515118a36108967af49d3f8105c95bc9 viewer-2-0-2-start
+87bfaf8c76f9b22d9c65d4b315358861be87c863 2-1-1-release
 b03065d018b8a2e28b7de85b293a4c992cb4c12d 2-1-release
 b8419565906e4feb434426d8d9b17dd1458e24b2 alpha-6
 bb38ff1a763738609e1b3cada6d15fa61e5e84b9 2-1-1-release
diff --git a/BuildParams b/BuildParams
index a602be2fe2f2d535852cba470aa71d6a6ee5fed4..0a193803da75dc9da03e5ac2552757a424868617 100644
--- a/BuildParams
+++ b/BuildParams
@@ -11,8 +11,11 @@ Linux.symbolfiles = "newview/secondlife-symbols-linux.tar.bz2"
 # Use Public Upload Locations
 public_build = true
 
+# skip windows debug build until we can get a fix in.
+build_CYGWIN_Debug = false
+
 # Update Public Inworld Build Status Indicators
-email_status_this_is_os = true
+email_status_this_is_os = false
 
 # Limit extent of codeticket updates to revisions after...
 codeticket_since = 2.2.0-release
@@ -89,6 +92,33 @@ brad-parabuild.email = brad@lindenlab.com
 brad-parabuild.build_server = false
 brad-parabuild.build_server_tests = false
 
+# ========================================
+# mesh-development
+# ========================================
+mesh-development.viewer_channel = "Project Viewer - Mesh"
+mesh-development.login_channel = "Project Viewer - Mesh"
+mesh-development.viewer_grid = aditi
+mesh-development.build_debug_release_separately = true
+mesh-development.build_CYGWIN_Debug = false
+mesh-development.build_viewer_update_version_manager = false
+
+# ========================================
+# viewer-mesh
+# ========================================
+
+viewer-mesh.build_viewer = true
+viewer-mesh.build_server = false
+viewer-mesh.build_Linux = true
+viewer-mesh.build_hg_bundle = true
+viewer-mesh.build_viewer_update_version_manager = false
+viewer-mesh.build_Debug = false
+viewer-mesh.build_RelWithDebInfo = false
+viewer-mesh.viewer_channel = "Project Viewer - Mesh"
+viewer-mesh.login_channel = "Project Viewer - Mesh"
+viewer-mesh.viewer_grid = aditi
+viewer-mesh.email = shining@lists.lindenlab.com
+
+
 # ========================================
 # CG
 # ========================================
diff --git a/autobuild.xml b/autobuild.xml
index cbaaae5c165b3e1fc22344cfb7f45a848095e9b1..0e4db1bdca3a51dabd5f0f1c9d41660f18712c93 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -3,14 +3,14 @@
 <map>
     <key>installables</key>
     <map>
-      <key>GL</key>
+      <key>GLOD</key>
       <map>
         <key>license</key>
-        <string>GL</string>
+        <string>GLOD</string>
         <key>license_file</key>
-        <string>LICENSES/GL.txt</string>
+        <string>LICENSES/glod.txt</string>
         <key>name</key>
-        <string>GL</string>
+        <string>GLOD</string>
         <key>platforms</key>
         <map>
           <key>darwin</key>
@@ -18,9 +18,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>0b7c1d43dc2b39301fef6c05948fb826</string>
+              <string>7c546f54f6ed654f713c778af3925dd4</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-darwin-20101004.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-glod/rev/226566/arch/Darwin/installer/glod-1.0pre4-darwin-20110413.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin</string>
@@ -30,9 +30,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>d85d88088360aee5b67105ad93b5ad16</string>
+              <string>18c708163d2a669bc3c030b05b4ebe61</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-linux-20100929.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-glod/rev/226657/arch/Linux/installer/glod-1.0pre4-linux-20110414.tar.bz2</string>
             </map>
             <key>name</key>
             <string>linux</string>
@@ -42,9 +42,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>f8d98cbe78d5aafbc7aaabf840325aaf</string>
+              <string>c4ae6cddc04e0b2908a301726a53922c</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-0.0.0-windows-20110303.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-glod/rev/226194/arch/CYGWIN/installer/glod-1.0pre4-windows-20110408.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -219,6 +219,54 @@
           </map>
         </map>
       </map>
+      <key>colladadom</key>
+      <map>
+        <key>license</key>
+        <string>scea</string>
+        <key>license_file</key>
+        <string>LICENSES/collada.txt</string>
+        <key>name</key>
+        <string>colladadom</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>480b27a0cb39a4adfcdeabef895de3e1</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-colladadom/rev/227230/arch/Darwin/installer/colladadom-2.2-darwin-20110420.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin</string>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>d05be8fc196e9ce7b6636b931cf13dff</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-colladadom/rev/226716/arch/Linux/installer/colladadom-2.2-linux-20110415.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>a9f548eb6f9aaf292508a8b09c7f2f73</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-colladadom/rev/226584/arch/CYGWIN/installer/colladadom-2.2-windows-20110413.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows</string>
+          </map>
+        </map>
+      </map>
       <key>curl</key>
       <map>
         <key>license</key>
@@ -531,6 +579,42 @@
           </map>
         </map>
       </map>
+      <key>glext</key>
+      <map>
+        <key>license</key>
+        <string>glext</string>
+        <key>license_file</key>
+        <string>LICENSES/glext.txt</string>
+        <key>name</key>
+        <string>glext</string>
+        <key>platforms</key>
+        <map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>5de58ca0fe19abf68b25956762ee0d29</string>
+              <key>url</key>
+              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glext-68-windows-20110406.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>5de58ca0fe19abf68b25956762ee0d29</string>
+              <key>url</key>
+              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glext-68-windows-20110406.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows</string>
+          </map>
+        </map>
+      </map>
       <key>glh_linear</key>
       <map>
         <key>license</key>
@@ -615,42 +699,6 @@
           </map>
         </map>
       </map>
-      <key>google-perftools</key>
-      <map>
-        <key>license</key>
-        <string>bsd</string>
-        <key>license_file</key>
-        <string>LICENSES/google-perftools.txt</string>
-        <key>name</key>
-        <string>google-perftools</string>
-        <key>platforms</key>
-        <map>
-          <key>linux</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>cf513fc2eec4a414cc804cf408932a45</string>
-              <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google_perftools-1.7-linux-20110315.tar.bz2</string>
-            </map>
-            <key>name</key>
-            <string>linux</string>
-          </map>
-          <key>windows</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>8108bffe1c814be9d035b47dac3d4541</string>
-              <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google-perftools-1.0-windows-20101001b.tar.bz2</string>
-            </map>
-            <key>name</key>
-            <string>windows</string>
-          </map>
-        </map>
-      </map>
       <key>google_breakpad</key>
       <map>
         <key>license</key>
@@ -704,7 +752,7 @@
         <key>license</key>
         <string>bsd</string>
         <key>license_file</key>
-        <string>LICENSES/gmock.txt</string>
+        <string>LICENSES/googlemock.txt</string>
         <key>name</key>
         <string>googlemock</string>
         <key>platforms</key>
@@ -776,7 +824,7 @@
         <key>license</key>
         <string>lgpl</string>
         <key>license_file</key>
-        <string>LICENSES/gtk.txt</string>
+        <string>LICENSES/gtk-atk-pango-glib.txt</string>
         <key>name</key>
         <string>gtk-atk-pango-glib</string>
         <key>platforms</key>
@@ -990,9 +1038,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>735a955e6442733e2342ab12c1087488</string>
+              <string>f194ba857ca8dd86483a3ef24535d0db</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.5.1-windows-20110221.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-libpng/rev/226532/arch/CYGWIN/installer/libpng-1.5.1-windows-20110413.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -1047,6 +1095,102 @@
           </map>
         </map>
       </map>
+      <key>llconvexdecomposition</key>
+      <map>
+        <key>license</key>
+        <string>havok</string>
+        <key>license_file</key>
+        <string>on_file</string>
+        <key>name</key>
+        <string>llconvexdecomposition</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>6e45ad68506cd1ba49fd35a3201f0478</string>
+              <key>url</key>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-llconvexdecomposition/rev/228821/arch/Darwin/installer/llconvexdecomposition-0.1-darwin-20110504.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin</string>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>00ff5144612c2e261a0811a4503ce3ba</string>
+              <key>url</key>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-llconvexdecomposition/rev/228821/arch/Linux/installer/llconvexdecomposition-0.1-linux-20110504.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>a4635dcbbe0915ce023dd41d3b848d4c</string>
+              <key>url</key>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-llconvexdecomposition/rev/228821/arch/CYGWIN/installer/llconvexdecomposition-0.1-windows-20110504.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows</string>
+          </map>
+        </map>
+      </map>
+      <key>llconvexdecompositionstub</key>
+      <map>
+        <key>license</key>
+        <string>lgpl</string>
+        <key>license_file</key>
+        <string>LICENSES/lgpl.txt</string>
+        <key>name</key>
+        <string>llconvexdecompositionstub</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>bc1388fc28dbb3bba1fe7cb8d09f49b4</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llconvexdecompositionstub/rev/227399/arch/Darwin/installer/llconvexdecompositionstub-0.3-darwin-20110421.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin</string>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>3295bd4a0514b7c15dda9044f40c175e</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llconvexdecompositionstub/rev/227399/arch/Linux/installer/llconvexdecompositionstub-0.3-linux-20110422.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>92f1dff3249024c1534b55343ed79ea3</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llconvexdecompositionstub/rev/227399/arch/CYGWIN/installer/llconvexdecompositionstub-0.3-windows-20110421.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows</string>
+          </map>
+        </map>
+      </map>
       <key>llqtwebkit</key>
       <map>
         <key>license</key>
@@ -1351,6 +1495,42 @@
           </map>
         </map>
       </map>
+      <key>pcre</key>
+      <map>
+        <key>license</key>
+        <string>bsd</string>
+        <key>license_file</key>
+        <string>LICENSES/pcre-license.txt</string>
+        <key>name</key>
+        <string>pcre</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>a8e74694a0f4248228c13c845ed0a6f8</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-pcre/rev/228822/arch/Darwin/installer/pcre-7.6-darwin-20110504.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin</string>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>bb0abe962b3b8208ed2dab0424aab33d</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-pcre/rev/228822/arch/Linux/installer/pcre-7.6-linux-20110504.tar.bz2</string>              
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+        </map>
+      </map>
       <key>quicktime</key>
       <map>
         <key>license</key>
@@ -1423,6 +1603,42 @@
           </map>
         </map>
       </map>
+      <key>tcmalloc</key>
+      <map>
+        <key>license</key>
+        <string>bsd</string>
+        <key>license_file</key>
+        <string>LICENSES/google-perftools.txt</string>
+        <key>name</key>
+        <string>tcmalloc</string>
+        <key>platforms</key>
+        <map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>dde928cb24d22a267004a8c17669ba65</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-perftools/rev/226426/arch/Linux/installer/google_perftools-1.7-linux-20110412.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>8308f7bd68bb7083655753b7abe7225f</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-perftools/rev/226287/arch/CYGWIN/installer/google_perftools-1.7-windows-20110411.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows</string>
+          </map>
+        </map>
+      </map>
       <key>tut</key>
       <map>
         <key>license</key>
@@ -1578,6 +1794,10 @@
             <map>
               <key>configure</key>
               <map>
+                <key>arguments</key>
+                <array>
+                  <string>../indra</string>
+                </array>
                 <key>command</key>
                 <string>cmake</string>
                 <key>options</key>
@@ -1587,10 +1807,6 @@
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
                 </array>
-                <key>arguments</key>
-                <array>
-                  <string>../indra</string>
-                </array>
               </map>
               <key>name</key>
               <string>DebugOS</string>
@@ -1619,6 +1835,10 @@
             <map>
               <key>configure</key>
               <map>
+                <key>arguments</key>
+                <array>
+                  <string>../indra</string>
+                </array>
                 <key>command</key>
                 <string>cmake</string>
                 <key>options</key>
@@ -1628,10 +1848,6 @@
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
                 </array>
-                <key>arguments</key>
-                <array>
-                  <string>../indra</string>
-                </array>
               </map>
               <key>name</key>
               <string>RelWithDebInfoOS</string>
@@ -1660,6 +1876,10 @@
             <map>
               <key>configure</key>
               <map>
+                <key>arguments</key>
+                <array>
+                  <string>../indra</string>
+                </array>
                 <key>command</key>
                 <string>cmake</string>
                 <key>options</key>
@@ -1669,10 +1889,6 @@
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
                 </array>
-                <key>arguments</key>
-                <array>
-                  <string>../indra</string>
-                </array>
               </map>
               <key>name</key>
               <string>ReleaseOS</string>
diff --git a/build.sh b/build.sh
index d46da2c66d9308a39a3a95118b32d00045d17a52..b322d30daf4dc470fc1829465d9a4fe631e5cfb2 100755
--- a/build.sh
+++ b/build.sh
@@ -32,19 +32,19 @@ build_dir_CYGWIN()
 
 installer_Darwin()
 {
-  ls -1td "$(build_dir_Darwin ${last_built_variant:-Release})/newview/"*.dmg 2>/dev/null | sed 1q
+  ls -1td "$(build_dir_Darwin Release)/newview/"*.dmg 2>/dev/null | sed 1q
 }
 
 installer_Linux()
 {
-  ls -1td "$(build_dir_Linux ${last_built_variant:-Release})/newview/"*.tar.bz2 2>/dev/null | sed 1q
+  ls -1td "$(build_dir_Linux Release)/newview/"*.tar.bz2 2>/dev/null | sed 1q
 }
 
 installer_CYGWIN()
 {
-  d=$(build_dir_CYGWIN ${last_built_variant:-Release})
-  p=$(sed 's:.*=::' "$d/newview/${last_built_variant:-Release}/touched.bat")
-  echo "$d/newview/${last_built_variant:-Release}/$p"
+  d=$(build_dir_CYGWIN Release)
+  p=$(sed 's:.*=::' "$d/newview/Release/touched.bat")
+  echo "$d/newview/Release/$p"
 }
 
 pre_build()
diff --git a/doc/contributions.txt b/doc/contributions.txt
index e7537b608b6ca6f16f31a2f3a3e92ae387091505..163667c8ade168ed275e087e8f7b764d81d704c2 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -196,6 +196,7 @@ blino Nakamura
 Boroondas Gupte
 	OPEN-29
 	OPEN-39
+	OPEN-39
 	SNOW-278
 	SNOW-503
 	SNOW-510
@@ -418,6 +419,7 @@ Jonathan Yap
 	VWR-17801
 	VWR-24347
 	STORM-975
+	STORM-990
 	STORM-1019
 	STORM-844
 	STORM-643
@@ -816,7 +818,9 @@ Thraxis Epsilon
 tiamat bingyi
 	CT-246
 Tofu Buzzard
+	CTS-411
 	STORM-546
+	VWR-24509
 TraductoresAnonimos Alter
 	CT-324
 Tue Torok
diff --git a/etc/message.xml b/etc/message.xml
index 764aea387945c6797d4b14a67e5df4c1f7f65397..3445975545dbd70c290c11f0d852ee14a0c58eab 100644
--- a/etc/message.xml
+++ b/etc/message.xml
@@ -596,6 +596,13 @@
 					<boolean>false</boolean>
 				</map>
 
+        <key>ObjectPhysicsProperties</key>
+        <map>
+          <key>flavor</key>
+          <string>llsd</string>
+          <key>trusted-sender</key>
+          <boolean>true</boolean>
+        </map>
 
 		  </map>
   	  	<key>capBans</key>
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index faffdc8ccdb524e6401b786b95205e1645a2774c..2c974fb4ff27ba5edab1aa868354e29972eeea3f 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -57,16 +57,18 @@ if (WINDOWS)
 
   add_definitions(
       /DLL_WINDOWS=1
+      /DDOM_DYNAMIC
       /DUNICODE
       /D_UNICODE 
       /GS
       /TP
-      /W3
+      /W2
       /c
       /Zc:forScope
       /nologo
       /Oy-
       /Zc:wchar_t-
+      /arch:SSE2
       )
      
   # Are we using the crummy Visual Studio KDU build workaround?
@@ -141,6 +143,8 @@ if (LINUX)
       -fno-strict-aliasing
       -fsigned-char
       -g
+      -msse2
+      -mfpmath=sse
       -pthread
       )
 
@@ -160,10 +164,6 @@ if (LINUX)
       link_directories(/usr/lib/mysql4/mysql)
     endif (EXISTS /usr/lib/mysql4/mysql)
 
-    add_definitions(
-        -msse2
-        -mfpmath=sse
-        )
   endif (SERVER)
 
   if (VIEWER)
@@ -171,6 +171,8 @@ if (LINUX)
     add_definitions(-fvisibility=hidden)
     # don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work.  Sigh!  The viewer doesn't need to catch SIGCHLD anyway.
     add_definitions(-DLL_IGNORE_SIGCHLD)
+    add_definitions(-march=pentium4 -mfpmath=sse)
+    #add_definitions(-ftree-vectorize) # THIS CRASHES GCC 3.1-3.2
     if (NOT STANDALONE)
       # this stops us requiring a really recent glibc at runtime
       add_definitions(-fno-stack-protector)
@@ -210,7 +212,7 @@ if (LINUX OR DARWIN)
     set(GCC_WARNINGS "${GCC_WARNINGS} -Werror")
   endif (NOT GCC_DISABLE_FATAL_WARNINGS)
 
-  set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder -Wno-non-virtual-dtor -Woverloaded-virtual")
+  set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder -Wno-non-virtual-dtor")
 
   set(CMAKE_C_FLAGS "${GCC_WARNINGS} ${CMAKE_C_FLAGS}")
   set(CMAKE_CXX_FLAGS "${GCC_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}")
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 89c1c3691ab5539efb0b5d99657c370c00261996..279d577a278a805f06442f2ba63bf580eadbed59 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -34,6 +34,7 @@ set(cmake_SOURCE_FILES
     FindZLIB.cmake
     FMOD.cmake
     FreeType.cmake
+    GLOD.cmake
     GStreamer010Plugin.cmake
     GooglePerfTools.cmake
     JPEG.cmake
@@ -41,6 +42,7 @@ set(cmake_SOURCE_FILES
     LLAudio.cmake
     LLCharacter.cmake
     LLCommon.cmake
+    LLConvexDecomposition.cmake
     LLCrashLogger.cmake
     LLDatabase.cmake
     LLImage.cmake
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 4202b54f9497f2e6920e361a7fd7b9c182dc7da6..d9efc8f40d2a20865ff3ece415e23e5c222a36fd 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -39,6 +39,8 @@ if(WINDOWS)
         libapriconv-1.dll
         ssleay32.dll
         libeay32.dll
+        libcollada14dom22-d.dll
+        glod.dll	
         )
 
     set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
@@ -49,6 +51,8 @@ if(WINDOWS)
         libapriconv-1.dll
         ssleay32.dll
         libeay32.dll
+        libcollada14dom22.dll
+        glod.dll
         )
 
     if(USE_GOOGLE_PERFTOOLS)
@@ -207,9 +211,12 @@ elseif(DARWIN)
         libaprutil-1.dylib
         libexpat.1.5.2.dylib
         libexpat.dylib
-        libllqtwebkit.dylib
+        libGLOD.dylib
+	libllqtwebkit.dylib
+	libminizip.a
         libndofdev.dylib
         libexception_handler.dylib
+	libcollada14dom.dylib
        )
 
     # fmod is statically linked on darwin
@@ -245,20 +252,23 @@ elseif(LINUX)
         libaprutil-1.so.0
         libatk-1.0.so
         libbreakpad_client.so.0
+       	libcollada14dom.so
         libcrypto.so.1.0.0
         libdb-5.1.so
         libexpat.so
         libexpat.so.1
+	libglod.so
         libgmock_main.so
         libgmock.so.0
         libgmodule-2.0.so
         libgobject-2.0.so
         libgtest_main.so
         libgtest.so.0
+	libminizip.so
         libopenal.so
         libopenjpeg.so
         libssl.so
-        libtcmalloc.so
+        libtcmalloc_minimal.so
         libuuid.so.16
         libuuid.so.16.0.22
         libssl.so.1.0.0
diff --git a/indra/cmake/FindTut.cmake b/indra/cmake/FindTut.cmake
deleted file mode 100644
index c2a9f430534aa1ee7e50ab87ed0bbe6b0cb241a2..0000000000000000000000000000000000000000
--- a/indra/cmake/FindTut.cmake
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- cmake -*-
-
-# - Find Tut
-# Find the Tut unit test framework includes and library
-# This module defines
-#  TUT_INCLUDE_DIR, where to find tut/tut.hpp.
-#  TUT_FOUND, If false, do not try to use Tut.
-
-find_path(TUT_INCLUDE_DIR tut/tut.hpp
-    NO_SYSTEM_ENVIRONMENT_PATH
-    )
-
-if (TUT_INCLUDE_DIR)
-  set(TUT_FOUND "YES")
-else (TUT_INCLUDE_DIR)
-  set(TUT_FOUND "NO")
-endif (TUT_INCLUDE_DIR)
-
-if (TUT_FOUND)
-  if (NOT TUT_FIND_QUIETLY)
-    message(STATUS "Found Tut: ${TUT_INCLUDE_DIR}")
-    set(TUT_FIND_QUIETLY TRUE) # Only alert us the first time
-  endif (NOT TUT_FIND_QUIETLY)
-else (TUT_FOUND)
-  if (TUT_FIND_REQUIRED)
-    message(FATAL_ERROR "Could not find Tut")
-  endif (TUT_FIND_REQUIRED)
-endif (TUT_FOUND)
-
-mark_as_advanced(TUT_INCLUDE_DIR)
diff --git a/indra/cmake/GLOD.cmake b/indra/cmake/GLOD.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..77221d55ede01056ed6b2e83228173907b064a03
--- /dev/null
+++ b/indra/cmake/GLOD.cmake
@@ -0,0 +1,9 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+if (NOT STANDALONE)
+  use_prebuilt_binary(GLOD)
+endif (NOT STANDALONE)
+
+set(GLOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
+set(GLOD_LIBRARIES glod)
diff --git a/indra/cmake/GooglePerfTools.cmake b/indra/cmake/GooglePerfTools.cmake
index 6c784a3a76984be4e58e2a30eb9cc57cee83883d..8740e36753d0f57a2e6a12b6bdcfe9a25c38defd 100644
--- a/indra/cmake/GooglePerfTools.cmake
+++ b/indra/cmake/GooglePerfTools.cmake
@@ -5,15 +5,16 @@ if (STANDALONE)
   include(FindGooglePerfTools)
 else (STANDALONE)
   if (WINDOWS)
-    use_prebuilt_binary(google-perftools)
+    use_prebuilt_binary(tcmalloc)
     set(TCMALLOC_LIBRARIES 
         debug libtcmalloc_minimal-debug
         optimized libtcmalloc_minimal)
     set(GOOGLE_PERFTOOLS_FOUND "YES")
   endif (WINDOWS)
   if (LINUX)
-    use_prebuilt_binary(google-perftools)
-    set(TCMALLOC_LIBRARIES tcmalloc)
+    use_prebuilt_binary(tcmalloc)
+    set(TCMALLOC_LIBRARIES 
+	tcmalloc)
     set(PROFILER_LIBRARIES profiler)
     set(GOOGLE_PERFTOOLS_INCLUDE_DIR
         ${LIBS_PREBUILT_DIR}/include)
@@ -28,12 +29,11 @@ if (GOOGLE_PERFTOOLS_FOUND)
 endif (GOOGLE_PERFTOOLS_FOUND)
 
 if (WINDOWS)
-    # *TODO -reenable this once we get server usage sorted out
-    #set(USE_GOOGLE_PERFTOOLS ON)
+    set(USE_GOOGLE_PERFTOOLS ON)
 endif (WINDOWS)
 
 if (USE_GOOGLE_PERFTOOLS)
-  set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
+  set(TCMALLOC_FLAG -ULL_USE_TCMALLOC=1)
   include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})
   set(GOOGLE_PERFTOOLS_LIBRARIES ${TCMALLOC_LIBRARIES} ${STACKTRACE_LIBRARIES} ${PROFILER_LIBRARIES})
 else (USE_GOOGLE_PERFTOOLS)
diff --git a/indra/cmake/LLConvexDecomposition.cmake b/indra/cmake/LLConvexDecomposition.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..8e445047823871af1d00759b83352d26f09dfd76
--- /dev/null
+++ b/indra/cmake/LLConvexDecomposition.cmake
@@ -0,0 +1,12 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+set(LLCONVEXDECOMP_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
+  
+if (INSTALL_PROPRIETARY AND NOT STANDALONE)
+  use_prebuilt_binary(llconvexdecomposition)
+  set(LLCONVEXDECOMP_LIBRARY llconvexdecomposition)
+else (INSTALL_PROPRIETARY AND NOT STANDALONE)
+  use_prebuilt_binary(llconvexdecompositionstub)
+  set(LLCONVEXDECOMP_LIBRARY llconvexdecompositionstub)
+endif (INSTALL_PROPRIETARY AND NOT STANDALONE)
diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake
index d397b78f1c91109bc6638aa31037ae38bc4ba2fd..e68d16ed085609592c85b84c2a6900283739171c 100644
--- a/indra/cmake/LLPrimitive.cmake
+++ b/indra/cmake/LLPrimitive.cmake
@@ -1,7 +1,33 @@
 # -*- cmake -*-
 
+# these should be moved to their own cmake file
+include(Prebuilt)
+use_prebuilt_binary(colladadom)
+use_prebuilt_binary(pcre)
+use_prebuilt_binary(libxml)
+
 set(LLPRIMITIVE_INCLUDE_DIRS
     ${LIBS_OPEN_DIR}/llprimitive
     )
+if (WINDOWS)
+	set(LLPRIMITIVE_LIBRARIES 
+        debug llprimitive
+        optimized llprimitive
+        debug libcollada14dom22-d
+        optimized libcollada14dom22
+        debug libboost_filesystem-vc100-mt-gd-1_45
+        optimized libboost_filesystem-vc100-mt-1_45
+        debug libboost_system-vc100-mt-gd-1_45
+        optimized libboost_system-vc100-mt-1_45
+        )
+else (WINDOWS)
+    set(LLPRIMITIVE_LIBRARIES 
+        llprimitive
+        collada14dom
+        minizip
+        xml2
+        pcrecpp
+        pcre
+        )
+endif (WINDOWS)
 
-set(LLPRIMITIVE_LIBRARIES llprimitive)
diff --git a/indra/cmake/LLTestCommand.cmake b/indra/cmake/LLTestCommand.cmake
index 554559edbd614a06c9dc59e4fe7c2ad985a0f0ab..b5a0580a904f6d93ff8c765e02e4e031555fed40 100644
--- a/indra/cmake/LLTestCommand.cmake
+++ b/indra/cmake/LLTestCommand.cmake
@@ -1,3 +1,4 @@
+include(Python)
 MACRO(LL_TEST_COMMAND OUTVAR LD_LIBRARY_PATH)
   # nat wonders how Kitware can use the term 'function' for a construct that
   # cannot return a value. And yet, variables you set inside a FUNCTION are
diff --git a/indra/cmake/OpenGL.cmake b/indra/cmake/OpenGL.cmake
index 661666f00d1317c74cbc1902a3fdf8fe04de7aa8..0a3dd976b43354adf1d5d2768356e95b004fc10e 100644
--- a/indra/cmake/OpenGL.cmake
+++ b/indra/cmake/OpenGL.cmake
@@ -2,8 +2,7 @@
 include(Prebuilt)
 
 if (NOT STANDALONE)
-  use_prebuilt_binary(GL)
-  # possible glh_linear should have its own .cmake file instead
+  use_prebuilt_binary(glext)
   use_prebuilt_binary(glh_linear)
   set(GLEXT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
 endif (NOT STANDALONE)
diff --git a/indra/cmake/Tut.cmake b/indra/cmake/Tut.cmake
index 738c08c42fe6c10e3c82d0e48994d095e9744271..7488e9dcb0b7c02c059faa342e975eb5d7c17ed1 100644
--- a/indra/cmake/Tut.cmake
+++ b/indra/cmake/Tut.cmake
@@ -1,11 +1,6 @@
 # -*- cmake -*-
 include(Prebuilt)
 
-set(TUT_FIND_REQUIRED TRUE)
-set(TUT_FIND_QUIETLY TRUE)
-
-if (STANDALONE)
-  include(FindTut)
-else (STANDALONE)
+if (NOT STANDALONE)
   use_prebuilt_binary(tut)
-endif (STANDALONE)
+endif(NOT STANDALONE)
diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake
index 2f23e7c307c6385953460269bef39dd3b512414b..cfccd29deff3db0b5ec60b8a8aa61bce2b31b89e 100644
--- a/indra/cmake/Variables.cmake
+++ b/indra/cmake/Variables.cmake
@@ -29,6 +29,7 @@ set(SERVER_PREFIX)
 set(VIEWER_PREFIX)
 set(INTEGRATION_TESTS_PREFIX)
 set(LL_TESTS ON CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation")
+set(INCREMENTAL_LINK OFF CACHE BOOL "Use incremental linking on win32 builds (enable for faster links on some machines)")
 
 if(LIBS_CLOSED_DIR)
   file(TO_CMAKE_PATH "${LIBS_CLOSED_DIR}" LIBS_CLOSED_DIR)
diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake
index 0f5a81c0203e4b1c867ad30fa278d7c4e7118c9b..7131445464469f9e88b1716e406e573ac0aac4fd 100644
--- a/indra/cmake/WebKitLibPlugin.cmake
+++ b/indra/cmake/WebKitLibPlugin.cmake
@@ -26,42 +26,45 @@ if (STANDALONE)
   endforeach(qlibname)
   # qjpeg depends on libjpeg
   list(APPEND QT_PLUGIN_LIBRARIES jpeg)
-  set(WEBKITLIBPLUGIN OFF CACHE BOOL
-      "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.")
+    set(WEBKITLIBPLUGIN OFF CACHE BOOL
+        "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.")
 else (STANDALONE)
-  use_prebuilt_binary(llqtwebkit)
-  set(WEBKITLIBPLUGIN ON CACHE BOOL
-      "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.")
+    use_prebuilt_binary(llqtwebkit)
+    set(WEBKITLIBPLUGIN ON CACHE BOOL
+        "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.")
 endif (STANDALONE)
 
 if (WINDOWS)
-  set(WEBKIT_PLUGIN_LIBRARIES
-      debug llqtwebkitd
-      debug QtWebKitd4
-      debug QtOpenGLd4
-      debug QtNetworkd4
-      debug QtGuid4
-      debug QtCored4
-      debug qtmaind
-      optimized llqtwebkit
-      optimized QtWebKit4
-      optimized QtOpenGL4
-      optimized QtNetwork4
-      optimized QtGui4
-      optimized QtCore4
-      optimized qtmain
-      )
+    set(WEBKIT_PLUGIN_LIBRARIES 
+    debug llqtwebkitd
+    debug QtWebKitd4
+    debug QtOpenGLd4
+    debug QtNetworkd4
+    debug QtGuid4
+    debug QtCored4
+    debug qtmaind
+    optimized llqtwebkit
+    optimized QtWebKit4
+    optimized QtOpenGL4
+    optimized QtNetwork4
+    optimized QtGui4
+    optimized QtCore4
+    optimized qtmain
+    )
 elseif (DARWIN)
-  set(WEBKIT_PLUGIN_LIBRARIES
-      optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib
-      debug ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib
-      )
+    set(WEBKIT_PLUGIN_LIBRARIES
+        optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib
+        debug ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib
+        )
 elseif (LINUX)
-  if (STANDALONE) 
     set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES} ${QT_PLUGIN_LIBRARIES})
-  else (STANDALONE)
     set(WEBKIT_PLUGIN_LIBRARIES
         llqtwebkit
+#        qico
+#        qpng
+#        qtiff
+#        qsvg
+#        QtSvg
         QtWebKit
         QtOpenGL
         QtNetwork
@@ -74,6 +77,9 @@ elseif (LINUX)
         X11
         Xrender
         GL
+
+#        sqlite3
+#        Xi
+#        SM
         )
-  endif (STANDALONE)
 endif (WINDOWS)
diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt
index 14841b5d3ddeb0af7068eb972e15242fd294abfd..6eb154458d5bc17dc36075a67237faeabc8482e9 100644
--- a/indra/llcharacter/CMakeLists.txt
+++ b/indra/llcharacter/CMakeLists.txt
@@ -77,12 +77,13 @@ list(APPEND llcharacter_SOURCE_FILES ${llcharacter_HEADER_FILES})
 add_library (llcharacter ${llcharacter_SOURCE_FILES})
 
 
-if(LL_TESTS)
-  # Add tests
-  include(LLAddBuildTest)
-  # UNIT TESTS
-  SET(llcharacter_TEST_SOURCE_FILES
-      lljoint.cpp
-      )
-  LL_ADD_PROJECT_UNIT_TESTS(llcharacter "${llcharacter_TEST_SOURCE_FILES}")
-endif(LL_TESTS)
+# Add tests
+if (LL_TESTS)
+	include(LLAddBuildTest)
+	# UNIT TESTS
+	SET(llcharacter_TEST_SOURCE_FILES
+	  lljoint.cpp
+	  )
+	LL_ADD_PROJECT_UNIT_TESTS(llcharacter "${llcharacter_TEST_SOURCE_FILES}")
+endif (LL_TESTS)
+
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index 5d750c6c967590b85d961ff77e80e4f7d716069a..19907933cb4c5cab89f20eb329be4f262351182e 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -50,6 +50,7 @@ LLJoint::LLJoint()
 	mUpdateXform = TRUE;
 	mJointNum = -1;
 	touch();
+	mResetAfterRestoreOldXform = false;
 }
 
 
@@ -234,6 +235,42 @@ void LLJoint::setPosition( const LLVector3& pos )
 }
 
 
+//--------------------------------------------------------------------
+// setPosition()
+//--------------------------------------------------------------------
+void LLJoint::setDefaultFromCurrentXform( void )
+{
+	mDefaultXform = mXform;
+	touch(MATRIX_DIRTY | POSITION_DIRTY);
+	
+}
+
+//--------------------------------------------------------------------
+// storeCurrentXform()
+//--------------------------------------------------------------------
+void LLJoint::storeCurrentXform( const LLVector3& pos )
+{
+	mOldXform = mXform;
+	mResetAfterRestoreOldXform = true;
+	setPosition( pos );
+}
+//--------------------------------------------------------------------
+// restoreOldXform()
+//--------------------------------------------------------------------
+void LLJoint::restoreOldXform( void )
+{
+	mResetAfterRestoreOldXform = false;
+	mXform = mOldXform;
+}
+//--------------------------------------------------------------------
+// restoreOldXform()
+//--------------------------------------------------------------------
+void LLJoint::restoreToDefaultXform( void )
+{	
+	mXform = mDefaultXform;
+	setPosition( mXform.getPosition() );	
+}
+
 //--------------------------------------------------------------------
 // getWorldPosition()
 //--------------------------------------------------------------------
@@ -522,3 +559,4 @@ void LLJoint::clampRotation(LLQuaternion old_rot, LLQuaternion new_rot)
 }
 
 // End
+
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 8c8e5930fba20d9ba7b8758fd80969707677a3c8..dc3c58cf645204c6667dfe62d403ba41e4a0c8b3 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -80,11 +80,16 @@ class LLJoint
 
 	// explicit transformation members
 	LLXformMatrix		mXform;
+	LLXformMatrix		mOldXform;
+	LLXformMatrix		mDefaultXform;
 
+	LLUUID				mId;
 public:
 	U32				mDirtyFlags;
 	BOOL			mUpdateXform;
 
+	BOOL			mResetAfterRestoreOldXform;
+
 	// describes the skin binding pose
 	LLVector3		mSkinOffset;
 
@@ -130,7 +135,9 @@ class LLJoint
 	// get/set local position
 	const LLVector3& getPosition();
 	void setPosition( const LLVector3& pos );
-
+	
+	void setDefaultPosition( const LLVector3& pos );
+	
 	// get/set world position
 	LLVector3 getWorldPosition();
 	LLVector3 getLastWorldPosition();
@@ -172,6 +179,21 @@ class LLJoint
 
 	S32 getJointNum() const { return mJointNum; }
 	void setJointNum(S32 joint_num) { mJointNum = joint_num; }
+	
+	void restoreOldXform( void );
+	void restoreToDefaultXform( void );
+	void setDefaultFromCurrentXform( void );
+	void storeCurrentXform( const LLVector3& pos );
+
+	//Accessor for the joint id
+	LLUUID getId( void ) { return mId; }
+	//Setter for the joints id
+	void setId( const LLUUID& id ) { mId = id;}
+
+	//If the old transform flag has been set, then the reset logic in avatar needs to be aware(test) of it
+	const BOOL doesJointNeedToBeReset( void ) const { return mResetAfterRestoreOldXform; }
+	//Setter for joint reset flag
+	void setJointToBeReset( BOOL val ) { mResetAfterRestoreOldXform = val; }
 };
 #endif // LL_LLJOINT_H
 
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
index 5b0867524ee99f0d0cca4e588fbe42924cba0d35..9df033a4ca592c47f9f71bb5eb4f39034899f8d5 100644
--- a/indra/llcharacter/llkeyframemotion.cpp
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -1145,7 +1145,7 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
 			constraint->mPositions[joint_num] = new_pos;
 		}
 		constraint->mFixupDistanceRMS *= 1.f / (constraint->mTotalLength * (F32)(shared_data->mChainLength - 1));
-		constraint->mFixupDistanceRMS = fsqrtf(constraint->mFixupDistanceRMS);
+		constraint->mFixupDistanceRMS = (F32) sqrt(constraint->mFixupDistanceRMS);
 
 		//reset old joint rots
 		for (joint_num = 0; joint_num <= shared_data->mChainLength; joint_num++)
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index eb610f625a31d7d14086a5c5bc5d220cc7309205..145dddd5434c418a2304f10769a049eb8c2f7d96 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -93,8 +93,9 @@ LLAssetDictionary::LLAssetDictionary()
 
 	addEntry(LLAssetType::AT_LINK, 				new AssetEntry("LINK",				"link",		"sym link",			false,		false,		true));
 	addEntry(LLAssetType::AT_LINK_FOLDER, 		new AssetEntry("FOLDER_LINK",		"link_f", 	"sym folder link",	false,		false,		true));
+	addEntry(LLAssetType::AT_MESH,              new AssetEntry("MESH",              "mesh",     "mesh",             false, false, false));
+	addEntry(LLAssetType::AT_NONE, 				new AssetEntry("NONE",				"-1",		NULL,		  		FALSE,		FALSE,		FALSE));
 
-	addEntry(LLAssetType::AT_NONE, 				new AssetEntry("NONE",				"-1",		NULL,		  		false,		false,		false));
 };
 
 // static
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index c5ff2364ccc4e97ddba02e6c207fbfadb91c2779..74ccd003244566be98a76ed0e78a309953314a1c 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -108,8 +108,10 @@ class LL_COMMON_API LLAssetType
 
 		AT_LINK_FOLDER = 25,
 			// Inventory folder link
-		
-		AT_COUNT = 26,
+		AT_MESH = 49,
+		    // Mesh data in our proprietary SLM format
+
+		AT_COUNT = 50,
 
 			// +*********************************************************+
 			// |  TO ADD AN ELEMENT TO THIS ENUM:                        |
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
index 6b38de65008f2f29b8689093c76e6d0f3fcca460..5a4b8325f427dd2bb7f5f72a04097f7487015eb7 100644
--- a/indra/llcommon/lldefs.h
+++ b/indra/llcommon/lldefs.h
@@ -236,5 +236,13 @@ inline LLDATATYPE llclampb(const LLDATATYPE& a)
 	return llmin(llmax(a, (LLDATATYPE)0), (LLDATATYPE)255);
 }
 
+template <class LLDATATYPE> 
+inline void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
+{
+	LLDATATYPE tmp = lhs;
+	lhs = rhs;
+	rhs = tmp;
+}
+
 #endif // LL_LLDEFS_H
 
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index 4ff93a553c1335bccfb00ce6fd23182565cf505f..2b25f2fabbd6883744b6ea36e122e994ef9b88a6 100644
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -27,140 +27,9 @@
 #ifndef LL_FASTTIMER_H
 #define LL_FASTTIMER_H
 
+// Implementation of getCPUClockCount32() and getCPUClockCount64 are now in llfastertimer_class.cpp.
+
 // pull in the actual class definition
 #include "llfasttimer_class.h"
 
-//
-// Important note: These implementations must be FAST!
-//
-
-#if LL_WINDOWS
-//
-// Windows implementation of CPU clock
-//
-
-//
-// NOTE: put back in when we aren't using platform sdk anymore
-//
-// because MS has different signatures for these functions in winnt.h
-// need to rename them to avoid conflicts
-//#define _interlockedbittestandset _renamed_interlockedbittestandset
-//#define _interlockedbittestandreset _renamed_interlockedbittestandreset
-//#include <intrin.h>
-//#undef _interlockedbittestandset
-//#undef _interlockedbittestandreset
-
-//inline U32 LLFastTimer::getCPUClockCount32()
-//{
-//	U64 time_stamp = __rdtsc();
-//	return (U32)(time_stamp >> 8);
-//}
-//
-//// return full timer value, *not* shifted by 8 bits
-//inline U64 LLFastTimer::getCPUClockCount64()
-//{
-//	return __rdtsc();
-//}
-
-// shift off lower 8 bits for lower resolution but longer term timing
-// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
-inline U32 LLFastTimer::getCPUClockCount32()
-{
-	U32 ret_val;
-	__asm
-	{
-        _emit   0x0f
-        _emit   0x31
-		shr eax,8
-		shl edx,24
-		or eax, edx
-		mov dword ptr [ret_val], eax
-	}
-    return ret_val;
-}
-
-// return full timer value, *not* shifted by 8 bits
-inline U64 LLFastTimer::getCPUClockCount64()
-{
-	U64 ret_val;
-	__asm
-	{
-        _emit   0x0f
-        _emit   0x31
-		mov eax,eax
-		mov edx,edx
-		mov dword ptr [ret_val+4], edx
-		mov dword ptr [ret_val], eax
-	}
-    return ret_val;
-}
-#endif
-
-
-#if (LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
-//
-// Linux and Solaris implementation of CPU clock - non-x86.
-// This is accurate but SLOW!  Only use out of desperation.
-//
-// Try to use the MONOTONIC clock if available, this is a constant time counter
-// with nanosecond resolution (but not necessarily accuracy) and attempts are
-// made to synchronize this value between cores at kernel start. It should not
-// be affected by CPU frequency. If not available use the REALTIME clock, but
-// this may be affected by NTP adjustments or other user activity affecting
-// the system time.
-inline U64 LLFastTimer::getCPUClockCount64()
-{
-	struct timespec tp;
-	
-#ifdef CLOCK_MONOTONIC // MONOTONIC supported at build-time?
-	if (-1 == clock_gettime(CLOCK_MONOTONIC,&tp)) // if MONOTONIC isn't supported at runtime then ouch, try REALTIME
-#endif
-		clock_gettime(CLOCK_REALTIME,&tp);
-
-	return (tp.tv_sec*LLFastTimer::sClockResolution)+tp.tv_nsec;        
-}
-
-inline U32 LLFastTimer::getCPUClockCount32()
-{
-	return (U32)(LLFastTimer::getCPUClockCount64() >> 8);
-}
-#endif // (LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
-
-
-#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
-//
-// Mac+Linux+Solaris FAST x86 implementation of CPU clock
-inline U32 LLFastTimer::getCPUClockCount32()
-{
-	U64 x;
-	__asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
-	return (U32)(x >> 8);
-}
-
-inline U64 LLFastTimer::getCPUClockCount64()
-{
-	U64 x;
-	__asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
-	return x;
-}
-#endif
-
-
-#if ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__)))
-//
-// Mac PPC (deprecated) implementation of CPU clock
-//
-// Just use gettimeofday implementation for now
-
-inline U32 LLFastTimer::getCPUClockCount32()
-{
-	return (U32)(get_clock_count()>>8);
-}
-
-inline U64 LLFastTimer::getCPUClockCount64()
-{
-	return get_clock_count();
-}
-#endif
-
 #endif // LL_LLFASTTIMER_H
diff --git a/indra/llcommon/llfasttimer_class.cpp b/indra/llcommon/llfasttimer_class.cpp
index bce87ada96907021cda0b24c156a0a350646424e..bd594b06cfd5094cd28aa3b1ec56ee59f755a954 100644
--- a/indra/llcommon/llfasttimer_class.cpp
+++ b/indra/llcommon/llfasttimer_class.cpp
@@ -35,10 +35,13 @@
 
 #include <boost/bind.hpp>
 
+
 #if LL_WINDOWS
+#include "lltimer.h"
 #elif LL_LINUX || LL_SOLARIS
 #include <sys/time.h>
 #include <sched.h>
+#include "lltimer.h"
 #elif LL_DARWIN
 #include <sys/time.h>
 #include "lltimer.h"	// get_clock_count()
@@ -61,6 +64,8 @@ BOOL LLFastTimer::sMetricLog = FALSE;
 LLMutex* LLFastTimer::sLogLock = NULL;
 std::queue<LLSD> LLFastTimer::sLogQueue;
 
+#define USE_RDTSC 0
+
 #if LL_LINUX || LL_SOLARIS
 U64 LLFastTimer::sClockResolution = 1000000000; // Nanosecond resolution
 #else
@@ -234,10 +239,23 @@ U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
 #else // windows or x86-mac or x86-linux or x86-solaris
 U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
 {
+#if USE_RDTSC || !LL_WINDOWS
 	//getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz
 	static U64 sCPUClockFrequency = U64(LLProcessorInfo().getCPUFrequency()*1000000.0);
 
 	// we drop the low-order byte in our timers, so report a lower frequency
+#else
+	// If we're not using RDTSC, each fasttimer tick is just a performance counter tick.
+	// Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency())
+	// since that would change displayed MHz stats for CPUs
+	static bool firstcall = true;
+	static U64 sCPUClockFrequency;
+	if (firstcall)
+	{
+		QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency);
+		firstcall = false;
+	}
+#endif
 	return sCPUClockFrequency >> 8;
 }
 #endif
@@ -482,6 +500,19 @@ void LLFastTimer::NamedTimer::resetFrame()
 {
 	if (sLog)
 	{ //output current frame counts to performance log
+
+		static S32 call_count = 0;
+		if (call_count % 100 == 0)
+		{
+			llinfos << "countsPerSecond (32 bit): " << countsPerSecond() << llendl;
+			llinfos << "get_clock_count (64 bit): " << get_clock_count() << llendl;
+			llinfos << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << llendl;
+			llinfos << "getCPUClockCount32() " << getCPUClockCount32() << llendl;
+			llinfos << "getCPUClockCount64() " << getCPUClockCount64() << llendl;
+			llinfos << "elapsed sec " << ((F64)getCPUClockCount64())/((F64)LLProcessorInfo().getCPUFrequency()*1000000.0) << llendl;
+		}
+		call_count++;
+
 		F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
 
 		F64 total_time = 0;
@@ -763,3 +794,144 @@ LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
 
 
 //////////////////////////////////////////////////////////////////////////////
+//
+// Important note: These implementations must be FAST!
+//
+
+
+#if LL_WINDOWS
+//
+// Windows implementation of CPU clock
+//
+
+//
+// NOTE: put back in when we aren't using platform sdk anymore
+//
+// because MS has different signatures for these functions in winnt.h
+// need to rename them to avoid conflicts
+//#define _interlockedbittestandset _renamed_interlockedbittestandset
+//#define _interlockedbittestandreset _renamed_interlockedbittestandreset
+//#include <intrin.h>
+//#undef _interlockedbittestandset
+//#undef _interlockedbittestandreset
+
+//inline U32 LLFastTimer::getCPUClockCount32()
+//{
+//	U64 time_stamp = __rdtsc();
+//	return (U32)(time_stamp >> 8);
+//}
+//
+//// return full timer value, *not* shifted by 8 bits
+//inline U64 LLFastTimer::getCPUClockCount64()
+//{
+//	return __rdtsc();
+//}
+
+// shift off lower 8 bits for lower resolution but longer term timing
+// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
+#if USE_RDTSC
+U32 LLFastTimer::getCPUClockCount32()
+{
+	U32 ret_val;
+	__asm
+	{
+        _emit   0x0f
+        _emit   0x31
+		shr eax,8
+		shl edx,24
+		or eax, edx
+		mov dword ptr [ret_val], eax
+	}
+    return ret_val;
+}
+
+// return full timer value, *not* shifted by 8 bits
+U64 LLFastTimer::getCPUClockCount64()
+{
+	U64 ret_val;
+	__asm
+	{
+        _emit   0x0f
+        _emit   0x31
+		mov eax,eax
+		mov edx,edx
+		mov dword ptr [ret_val+4], edx
+		mov dword ptr [ret_val], eax
+	}
+    return ret_val;
+}
+
+std::string LLFastTimer::sClockType = "rdtsc";
+
+#else
+//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
+// These use QueryPerformanceCounter, which is arguably fine and also works on amd architectures.
+U32 LLFastTimer::getCPUClockCount32()
+{
+	return (U32)(get_clock_count()>>8);
+}
+
+U64 LLFastTimer::getCPUClockCount64()
+{
+	return get_clock_count();
+}
+
+std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
+#endif
+
+#endif
+
+
+#if (LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
+//
+// Linux and Solaris implementation of CPU clock - non-x86.
+// This is accurate but SLOW!  Only use out of desperation.
+//
+// Try to use the MONOTONIC clock if available, this is a constant time counter
+// with nanosecond resolution (but not necessarily accuracy) and attempts are
+// made to synchronize this value between cores at kernel start. It should not
+// be affected by CPU frequency. If not available use the REALTIME clock, but
+// this may be affected by NTP adjustments or other user activity affecting
+// the system time.
+U64 LLFastTimer::getCPUClockCount64()
+{
+	struct timespec tp;
+	
+#ifdef CLOCK_MONOTONIC // MONOTONIC supported at build-time?
+	if (-1 == clock_gettime(CLOCK_MONOTONIC,&tp)) // if MONOTONIC isn't supported at runtime then ouch, try REALTIME
+#endif
+		clock_gettime(CLOCK_REALTIME,&tp);
+
+	return (tp.tv_sec*LLFastTimer::sClockResolution)+tp.tv_nsec;        
+}
+
+U32 LLFastTimer::getCPUClockCount32()
+{
+	return (U32)(LLFastTimer::getCPUClockCount64() >> 8);
+}
+
+std::string LLFastTimer::sClockType = "clock_gettime";
+
+#endif // (LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
+
+
+#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
+//
+// Mac+Linux+Solaris FAST x86 implementation of CPU clock
+U32 LLFastTimer::getCPUClockCount32()
+{
+	U64 x;
+	__asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+	return (U32)(x >> 8);
+}
+
+U64 LLFastTimer::getCPUClockCount64()
+{
+	U64 x;
+	__asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+	return x;
+}
+
+std::string LLFastTimer::sClockType = "rdtsc";
+#endif
+
diff --git a/indra/llcommon/llfasttimer_class.h b/indra/llcommon/llfasttimer_class.h
index eb9789682bb01bab79acf56d47f445bf83d29e08..827747f0c686fc87f6cc56776a7cd741f156608e 100644
--- a/indra/llcommon/llfasttimer_class.h
+++ b/indra/llcommon/llfasttimer_class.h
@@ -31,12 +31,15 @@
 
 #define FAST_TIMER_ON 1
 #define TIME_FAST_TIMERS 0
+#define DEBUG_FAST_TIMER_THREADS 1
 
 class LLMutex;
 
 #include <queue>
 #include "llsd.h"
 
+LL_COMMON_API void assert_main_thread();
+
 class LL_COMMON_API LLFastTimer
 {
 public:
@@ -175,6 +178,11 @@ class LL_COMMON_API LLFastTimer
 #if TIME_FAST_TIMERS
 		U64 timer_end = getCPUClockCount64();
 		sTimerCycles += timer_end - timer_start;
+#endif
+#if DEBUG_FAST_TIMER_THREADS
+#if !LL_RELEASE
+		assert_main_thread();
+#endif
 #endif
 	}
 
@@ -245,6 +253,7 @@ class LL_COMMON_API LLFastTimer
 		U32				mChildTime;
 	};
 	static CurTimerData		sCurTimerData;
+	static std::string sClockType;
 
 private:
 	static U32 getCPUClockCount32();
diff --git a/indra/llcommon/llfoldertype.cpp b/indra/llcommon/llfoldertype.cpp
index ebc79af412c22460300b56fa6b8fe58738cc506b..c2cfb7286e242478506c338335e19caa0cb495bb 100644
--- a/indra/llcommon/llfoldertype.cpp
+++ b/indra/llcommon/llfoldertype.cpp
@@ -89,6 +89,9 @@ LLFolderDictionary::LLFolderDictionary()
 	addEntry(LLFolderType::FT_CURRENT_OUTFIT, 		new FolderEntry("current",	TRUE));
 	addEntry(LLFolderType::FT_OUTFIT, 				new FolderEntry("outfit",	FALSE));
 	addEntry(LLFolderType::FT_MY_OUTFITS, 			new FolderEntry("my_otfts",	TRUE));
+
+	addEntry(LLFolderType::FT_MESH, 				new FolderEntry("mesh",	TRUE));
+
 	addEntry(LLFolderType::FT_INBOX, 				new FolderEntry("inbox",	TRUE));
 		 
 	addEntry(LLFolderType::FT_NONE, 				new FolderEntry("-1",		FALSE));
diff --git a/indra/llcommon/llfoldertype.h b/indra/llcommon/llfoldertype.h
index 936fbed17d204bb982a84cbfca8a63efd5602de8..cb32cb075b533957d6d3e7e785fa2a5946f1d3af 100644
--- a/indra/llcommon/llfoldertype.h
+++ b/indra/llcommon/llfoldertype.h
@@ -80,9 +80,11 @@ class LL_COMMON_API LLFolderType
 		FT_OUTFIT = 47,
 		FT_MY_OUTFITS = 48,
 		
-		FT_INBOX = 49,
+		FT_MESH = 49,
 
-		FT_COUNT = 50,
+		FT_INBOX = 50,
+
+		FT_COUNT = 51,
 
 		FT_NONE = -1
 	};
diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h
index c8acbbe5917c8cba398ddd9bc9071b5231990dfa..1526e6ac3c0d5ea50b3c0d0862ac491e5f3c6972 100644
--- a/indra/llcommon/llmd5.h
+++ b/indra/llcommon/llmd5.h
@@ -103,7 +103,7 @@ class LL_COMMON_API LLMD5 {
   void				raw_digest(unsigned char *array) const;	// provide 16-byte array for binary data
   void				hex_digest(char *string) const;			// provide 33-byte array for ascii-hex string
 
-  friend std::ostream&   operator<< (std::ostream&, LLMD5 context);
+  friend LL_COMMON_API std::ostream&   operator<< (std::ostream&, LLMD5 context);
 
 private:
 
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 51fcd5b7177b8150709b35a11dbd8b21fb30a676..21d1c84d695d4a5868e21fc1825f05db63db58d9 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -71,25 +71,6 @@ void LLMemory::freeReserve()
 	reserveMem = NULL;
 }
 
-void* ll_allocate (size_t size)
-{
-	if (size == 0)
-	{
-		llwarns << "Null allocation" << llendl;
-	}
-	void *p = malloc(size);
-	if (p == NULL)
-	{
-		LLMemory::freeReserve();
-		llerrs << "Out of memory Error" << llendl;
-	}
-	return p;
-}
-
-void ll_release (void *p)
-{
-	free(p);
-}
 
 //----------------------------------------------------------------------------
 
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 11406f59b016eb656fd167e604f0230e0e4ddd2c..3bd14035768e31b1859e33645f4924d8319cffed 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -1,25 +1,25 @@
-/** 
+/**
  * @file llmemory.h
  * @brief Memory allocation/deallocation header-stuff goes here.
  *
  * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, Linden Research, Inc.
- * 
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation;
  * version 2.1 of the License only.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
+ *
  * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  * $/LicenseInfo$
  */
@@ -28,12 +28,82 @@
 
 #include "llmemtype.h"
 
-extern S32 gTotalDAlloc;
-extern S32 gTotalDAUse;
-extern S32 gDACount;
+#if LL_DEBUG
+inline void* ll_aligned_malloc( size_t size, int align )
+{
+	void* mem = malloc( size + (align - 1) + sizeof(void*) );
+	char* aligned = ((char*)mem) + sizeof(void*);
+	aligned += align - ((uintptr_t)aligned & (align - 1));
+
+	((void**)aligned)[-1] = mem;
+	return aligned;
+}
+
+inline void ll_aligned_free( void* ptr )
+{
+	free( ((void**)ptr)[-1] );
+}
 
-extern void* ll_allocate (size_t size);
-extern void ll_release (void *p);
+inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16().
+{
+#if defined(LL_WINDOWS)
+	return _mm_malloc(size, 16);
+#elif defined(LL_DARWIN)
+	return malloc(size); // default osx malloc is 16 byte aligned.
+#else
+	void *rtn;
+	if (LL_LIKELY(0 == posix_memalign(&rtn, 16, size)))
+		return rtn;
+	else // bad alignment requested, or out of memory
+		return NULL;
+#endif
+}
+
+inline void ll_aligned_free_16(void *p)
+{
+#if defined(LL_WINDOWS)
+	_mm_free(p);
+#elif defined(LL_DARWIN)
+	return free(p);
+#else
+	free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+
+inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32().
+{
+#if defined(LL_WINDOWS)
+	return _mm_malloc(size, 32);
+#elif defined(LL_DARWIN)
+	return ll_aligned_malloc( size, 32 );
+#else
+	void *rtn;
+	if (LL_LIKELY(0 == posix_memalign(&rtn, 32, size)))
+		return rtn;
+	else // bad alignment requested, or out of memory
+		return NULL;
+#endif
+}
+
+inline void ll_aligned_free_32(void *p)
+{
+#if defined(LL_WINDOWS)
+	_mm_free(p);
+#elif defined(LL_DARWIN)
+	ll_aligned_free( p );
+#else
+	free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+#else // LL_DEBUG
+// ll_aligned_foo are noops now that we use tcmalloc everywhere (tcmalloc aligns automatically at appropriate intervals)
+#define ll_aligned_malloc( size, align ) malloc(size)
+#define ll_aligned_free( ptr ) free(ptr)
+#define ll_aligned_malloc_16 malloc
+#define ll_aligned_free_16 free
+#define ll_aligned_malloc_32 malloc
+#define ll_aligned_free_32 free
+#endif // LL_DEBUG
 
 class LL_COMMON_API LLMemory
 {
diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp
index 55d0c85cbd051cb5a83fd573eac36e46291c2363..e1876599fccbc6a3154c5d1da5c7caf38ceda3ef 100644
--- a/indra/llcommon/llrefcount.cpp
+++ b/indra/llcommon/llrefcount.cpp
@@ -29,9 +29,25 @@
 
 #include "llerror.h"
 
+#if LL_REF_COUNT_DEBUG
+#include "llthread.h"
+#include "llapr.h"
+#endif
+
 LLRefCount::LLRefCount(const LLRefCount& other)
 :	mRef(0)
 {
+#if LL_REF_COUNT_DEBUG
+	if(gAPRPoolp)
+	{
+		mMutexp = new LLMutex(gAPRPoolp) ;
+	}
+	else
+	{
+		mMutexp = NULL ;
+	}
+	mCrashAtUnlock = FALSE ;
+#endif
 }
 
 LLRefCount& LLRefCount::operator=(const LLRefCount&)
@@ -43,6 +59,17 @@ LLRefCount& LLRefCount::operator=(const LLRefCount&)
 LLRefCount::LLRefCount() :
 	mRef(0)
 {
+#if LL_REF_COUNT_DEBUG
+	if(gAPRPoolp)
+	{
+		mMutexp = new LLMutex(gAPRPoolp) ;
+	}
+	else
+	{
+		mMutexp = NULL ;
+	}
+	mCrashAtUnlock = FALSE ;
+#endif
 }
 
 LLRefCount::~LLRefCount()
@@ -51,4 +78,87 @@ LLRefCount::~LLRefCount()
 	{
 		llerrs << "deleting non-zero reference" << llendl;
 	}
+
+#if LL_REF_COUNT_DEBUG
+	if(gAPRPoolp)
+	{
+		delete mMutexp ;
+	}
+#endif
 }
+
+#if LL_REF_COUNT_DEBUG
+void LLRefCount::ref() const
+{ 
+	if(mMutexp)
+	{
+		if(mMutexp->isLocked()) 
+		{
+			mCrashAtUnlock = TRUE ;
+			llerrs << "the mutex is locked by the thread: " << mLockedThreadID 
+				<< " Current thread: " << LLThread::currentID() << llendl ;
+		}
+
+		mMutexp->lock() ;
+		mLockedThreadID = LLThread::currentID() ;
+
+		mRef++; 
+
+		if(mCrashAtUnlock)
+		{
+			while(1); //crash here.
+		}
+		mMutexp->unlock() ;
+	}
+	else
+	{
+		mRef++; 
+	}
+} 
+
+S32 LLRefCount::unref() const
+{
+	if(mMutexp)
+	{
+		if(mMutexp->isLocked()) 
+		{
+			mCrashAtUnlock = TRUE ;
+			llerrs << "the mutex is locked by the thread: " << mLockedThreadID 
+				<< " Current thread: " << LLThread::currentID() << llendl ;
+		}
+
+		mMutexp->lock() ;
+		mLockedThreadID = LLThread::currentID() ;
+		
+		llassert(mRef >= 1);
+		if (0 == --mRef) 
+		{
+			if(mCrashAtUnlock)
+			{
+				while(1); //crash here.
+			}
+			mMutexp->unlock() ;
+
+			delete this; 
+			return 0;
+		}
+
+		if(mCrashAtUnlock)
+		{
+			while(1); //crash here.
+		}
+		mMutexp->unlock() ;
+		return mRef;
+	}
+	else
+	{
+		llassert(mRef >= 1);
+		if (0 == --mRef) 
+		{
+			delete this; 
+			return 0;
+		}
+		return mRef;
+	}
+}	
+#endif
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index 19f008b15ccf43af020492d6c51108ffe4d45a96..8eb5d53f3f926324bfacda4ec4bc519a42fb571e 100644
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -28,6 +28,11 @@
 
 #include <boost/noncopyable.hpp>
 
+#define LL_REF_COUNT_DEBUG 0
+#if LL_REF_COUNT_DEBUG
+class LLMutex ;
+#endif
+
 //----------------------------------------------------------------------------
 // RefCount objects should generally only be accessed by way of LLPointer<>'s
 // see llthread.h for LLThreadSafeRefCount
@@ -43,12 +48,16 @@ class LL_COMMON_API LLRefCount
 public:
 	LLRefCount();
 
-	void ref() const
+#if LL_REF_COUNT_DEBUG
+	void ref() const ;
+	S32 unref() const ;
+#else
+	inline void ref() const
 	{ 
 		mRef++; 
 	} 
 
-	S32 unref() const
+	inline S32 unref() const
 	{
 		llassert(mRef >= 1);
 		if (0 == --mRef) 
@@ -58,6 +67,7 @@ class LL_COMMON_API LLRefCount
 		}
 		return mRef;
 	}	
+#endif
 
 	//NOTE: when passing around a const LLRefCount object, this can return different results
 	// at different types, since mRef is mutable
@@ -68,6 +78,12 @@ class LL_COMMON_API LLRefCount
 
 private: 
 	mutable S32	mRef; 
+
+#if LL_REF_COUNT_DEBUG
+	LLMutex*  mMutexp ;
+	mutable U32  mLockedThreadID ;
+	mutable BOOL mCrashAtUnlock ; 
+#endif
 };
 
 #endif
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index 10f460e8a6d0baf2ba038b517d093986659969be..5be5ecc492f6795e91e13660f49f677232e0110e 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -34,6 +34,12 @@
 #include <iostream>
 #include "apr_base64.h"
 
+#ifdef LL_STANDALONE
+# include <zlib.h>
+#else
+# include "zlib/zlib.h"  // for davep's dirty little zip functions
+#endif
+
 #if !LL_WINDOWS
 #include <netinet/in.h> // htonl & ntohl
 #endif
@@ -1983,3 +1989,180 @@ std::ostream& operator<<(std::ostream& s, const LLSD& llsd)
 	return s;
 }
 
+
+//dirty little zippers -- yell at davep if these are horrid
+
+//return a string containing gzipped bytes of binary serialized LLSD
+// VERY inefficient -- creates several copies of LLSD block in memory
+std::string zip_llsd(LLSD& data)
+{ 
+	std::stringstream llsd_strm;
+
+	LLSDSerialize::toBinary(data, llsd_strm);
+
+	const U32 CHUNK = 65536;
+
+	z_stream strm;
+	strm.zalloc = Z_NULL;
+	strm.zfree = Z_NULL;
+	strm.opaque = Z_NULL;
+
+	S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION);
+	if (ret != Z_OK)
+	{
+		llwarns << "Failed to compress LLSD block." << llendl;
+		return std::string();
+	}
+
+	std::string source = llsd_strm.str();
+
+	U8 out[CHUNK];
+
+	strm.avail_in = source.size();
+	strm.next_in = (U8*) source.data();
+	U8* output = NULL;
+
+	U32 cur_size = 0;
+
+	U32 have = 0;
+
+	do
+	{
+		strm.avail_out = CHUNK;
+		strm.next_out = out;
+
+		ret = deflate(&strm, Z_FINISH);
+		if (ret == Z_OK || ret == Z_STREAM_END)
+		{ //copy result into output
+			if (strm.avail_out >= CHUNK)
+			{
+				llerrs << "WTF?" << llendl;
+			}
+
+			have = CHUNK-strm.avail_out;
+			output = (U8*) realloc(output, cur_size+have);
+			memcpy(output+cur_size, out, have);
+			cur_size += have;
+		}
+		else 
+		{
+			free(output);
+			llwarns << "Failed to compress LLSD block." << llendl;
+			return std::string();
+		}
+	}
+	while (ret == Z_OK);
+
+	std::string::size_type size = cur_size;
+
+	std::string result((char*) output, size);
+	deflateEnd(&strm);
+	free(output);
+
+#if 0 //verify results work with unzip_llsd
+	std::istringstream test(result);
+	LLSD test_sd;
+	if (!unzip_llsd(test_sd, test, result.size()))
+	{
+		llerrs << "Invalid compression result!" << llendl;
+	}
+#endif
+
+	return result;
+}
+
+//decompress a block of LLSD from provided istream
+// not very efficient -- creats a copy of decompressed LLSD block in memory
+// and deserializes from that copy using LLSDSerialize
+bool unzip_llsd(LLSD& data, std::istream& is, S32 size)
+{
+	U8* result = NULL;
+	U32 cur_size = 0;
+	z_stream strm;
+		
+	const U32 CHUNK = 65536;
+
+	U8 *in = new U8[size];
+	is.read((char*) in, size); 
+
+	U8 out[CHUNK];
+		
+	strm.zalloc = Z_NULL;
+	strm.zfree = Z_NULL;
+	strm.opaque = Z_NULL;
+	strm.avail_in = size;
+	strm.next_in = in;
+
+	S32 ret = inflateInit(&strm);
+
+	do
+	{
+		strm.avail_out = CHUNK;
+		strm.next_out = out;
+		ret = inflate(&strm, Z_NO_FLUSH);
+		if (ret == Z_STREAM_ERROR)
+		{
+			inflateEnd(&strm);
+			free(result);
+			delete [] in;
+			return false;
+		}
+		
+		switch (ret)
+		{
+		case Z_NEED_DICT:
+			ret = Z_DATA_ERROR;
+		case Z_DATA_ERROR:
+		case Z_MEM_ERROR:
+			inflateEnd(&strm);
+			free(result);
+			delete [] in;
+			return false;
+			break;
+		}
+
+		U32 have = CHUNK-strm.avail_out;
+
+		result = (U8*) realloc(result, cur_size + have);
+		memcpy(result+cur_size, out, have);
+		cur_size += have;
+
+	} while (ret == Z_OK);
+
+	inflateEnd(&strm);
+	delete [] in;
+
+	if (ret != Z_STREAM_END)
+	{
+		free(result);
+		return false;
+	}
+
+	//result now points to the decompressed LLSD block
+	{
+		std::string res_str((char*) result, cur_size);
+
+		std::string deprecated_header("<? LLSD/Binary ?>");
+
+		if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
+		{
+			res_str = res_str.substr(deprecated_header.size()+1, cur_size);
+		}
+		cur_size = res_str.size();
+
+		std::istringstream istr(res_str);
+		
+		if (!LLSDSerialize::fromBinary(data, istr, cur_size))
+		{
+			llwarns << "Failed to unzip LLSD block" << llendl;
+			free(result);
+			return false;
+		}
+	}
+
+	free(result);
+	return true;
+}
+
+
+
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
index 1f096b52542125bcaaa13e2589305c84fa1fbf43..99a3ea3cd48c77f9970f35198c06b7975187fa65 100644
--- a/indra/llcommon/llsdserialize.h
+++ b/indra/llcommon/llsdserialize.h
@@ -790,4 +790,8 @@ class LL_COMMON_API LLSDSerialize
 	}
 };
 
+//dirty little zip functions -- yell at davep
+LL_COMMON_API std::string zip_llsd(LLSD& data);
+LL_COMMON_API bool unzip_llsd(LLSD& data, std::istream& is, S32 size);
+
 #endif // LL_LLSDSERIALIZE_H
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index 49d05ef4114ddd5fb3df5c8a777e0c13dbaa4e38..d9400fb5b325f1cbe05f57fb477def27eab36b86 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -56,6 +56,21 @@
 // 
 //----------------------------------------------------------------------------
 
+#if !LL_DARWIN
+U32 ll_thread_local sThreadID = 0;
+#endif 
+
+U32 LLThread::sIDIter = 0;
+
+LL_COMMON_API void assert_main_thread()
+{
+	static U32 s_thread_id = LLThread::currentID();
+	if (LLThread::currentID() != s_thread_id)
+	{
+		llerrs << "Illegal execution outside main thread." << llendl;
+	}
+}
+
 //
 // Handed to the APR thread creation function
 //
@@ -63,10 +78,14 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
 {
 	LLThread *threadp = (LLThread *)datap;
 
+#if !LL_DARWIN
+	sThreadID = threadp->mID;
+#endif
+
 	// Run the user supplied function
 	threadp->run();
 
-	llinfos << "LLThread::staticRun() Exiting: " << threadp->mName << llendl;
+	//llinfos << "LLThread::staticRun() Exiting: " << threadp->mName << llendl;
 	
 	// We're done with the run function, this thread is done executing now.
 	threadp->mStatus = STOPPED;
@@ -81,6 +100,8 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
 	mAPRThreadp(NULL),
 	mStatus(STOPPED)
 {
+	mID = ++sIDIter;
+
 	// Thread creation probably CAN be paranoid about APR being initialized, if necessary
 	if (poolp)
 	{
@@ -121,7 +142,7 @@ void LLThread::shutdown()
 			// First, set the flag that indicates that we're ready to die
 			setQuitting();
 
-			llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl;
+			//llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl;
 			// Now wait a bit for the thread to exit
 			// It's unclear whether I should even bother doing this - this destructor
 			// should netver get called unless we're already stopped, really...
@@ -143,7 +164,7 @@ void LLThread::shutdown()
 		if (!isStopped())
 		{
 			// This thread just wouldn't stop, even though we gave it time
-			llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+			//llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
 			// Put a stake in its heart.
 			apr_thread_exit(mAPRThreadp, -1);
 			return;
@@ -283,7 +304,7 @@ void LLThread::wakeLocked()
 //============================================================================
 
 LLMutex::LLMutex(apr_pool_t *poolp) :
-	mAPRMutexp(NULL)
+	mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD)
 {
 	//if (poolp)
 	//{
@@ -315,7 +336,18 @@ LLMutex::~LLMutex()
 
 void LLMutex::lock()
 {
+#if LL_DARWIN
+	if (mLockingThread == LLThread::currentID())
+#else
+	if (mLockingThread == sThreadID)
+#endif
+	{ //redundant lock
+		mCount++;
+		return;
+	}
+	
 	apr_thread_mutex_lock(mAPRMutexp);
+	
 #if MUTEX_DEBUG
 	// Have to have the lock before we can access the debug info
 	U32 id = LLThread::currentID();
@@ -323,10 +355,22 @@ void LLMutex::lock()
 		llerrs << "Already locked in Thread: " << id << llendl;
 	mIsLocked[id] = TRUE;
 #endif
+
+#if LL_DARWIN
+	mLockingThread = LLThread::currentID();
+#else
+	mLockingThread = sThreadID;
+#endif
 }
 
 void LLMutex::unlock()
 {
+	if (mCount > 0)
+	{ //not the root unlock
+		mCount--;
+		return;
+	}
+	
 #if MUTEX_DEBUG
 	// Access the debug info while we have the lock
 	U32 id = LLThread::currentID();
@@ -334,6 +378,8 @@ void LLMutex::unlock()
 		llerrs << "Not locked in Thread: " << id << llendl;	
 	mIsLocked[id] = FALSE;
 #endif
+
+	mLockingThread = NO_THREAD;
 	apr_thread_mutex_unlock(mAPRMutexp);
 }
 
@@ -351,6 +397,11 @@ bool LLMutex::isLocked()
 	}
 }
 
+U32 LLMutex::lockingThread() const
+{
+	return mLockingThread;
+}
+
 //============================================================================
 
 LLCondition::LLCondition(apr_pool_t *poolp) :
@@ -371,6 +422,15 @@ LLCondition::~LLCondition()
 
 void LLCondition::wait()
 {
+	if (!isLocked())
+	{ //mAPRMutexp MUST be locked before calling apr_thread_cond_wait
+		apr_thread_mutex_lock(mAPRMutexp);
+#if MUTEX_DEBUG
+		// avoid asserts on destruction in non-release builds
+		U32 id = LLThread::currentID();
+		mIsLocked[id] = TRUE;
+#endif
+	}
 	apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
 }
 
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index f1c6cd75af30a2922760bd9eee85fa30e8fbeb98..40291a25693717f212f835c9909ceb48ad79cd0d 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -35,8 +35,17 @@ class LLThread;
 class LLMutex;
 class LLCondition;
 
+#if LL_WINDOWS
+#define ll_thread_local __declspec(thread)
+#else
+#define ll_thread_local __thread
+#endif
+
 class LL_COMMON_API LLThread
 {
+private:
+	static U32 sIDIter;
+
 public:
 	typedef enum e_thread_status
 	{
@@ -77,6 +86,8 @@ class LL_COMMON_API LLThread
 	apr_pool_t *getAPRPool() { return mAPRPoolp; }
 	LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; }
 
+	U32 getID() const { return mID; }
+
 private:
 	BOOL				mPaused;
 	
@@ -91,6 +102,7 @@ class LL_COMMON_API LLThread
 	apr_pool_t			*mAPRPoolp;
 	BOOL				mIsLocalPool;
 	EThreadStatus		mStatus;
+	U32					mID;
 
 	//a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used.
 	//Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes.
@@ -128,17 +140,27 @@ class LL_COMMON_API LLThread
 class LL_COMMON_API LLMutex
 {
 public:
+	typedef enum
+	{
+		NO_THREAD = 0xFFFFFFFF
+	} e_locking_thread;
+
 	LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
 	virtual ~LLMutex();
 	
 	void lock();		// blocks
 	void unlock();
 	bool isLocked(); 	// non-blocking, but does do a lock/unlock so not free
+	U32 lockingThread() const; //get ID of locking thread
 	
 protected:
 	apr_thread_mutex_t *mAPRMutexp;
+	mutable U32			mCount;
+	mutable U32			mLockingThread;
+	
 	apr_pool_t			*mAPRPoolp;
 	BOOL				mIsLocalPool;
+	
 #if MUTEX_DEBUG
 	std::map<U32, BOOL> mIsLocked;
 #endif
diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h
index 9f86de124e8bc17d5e23b22adce9a3491235d1c6..556eff8370cae1196e9b116ff652b8a7c53a6c04 100644
--- a/indra/llcommon/stdenums.h
+++ b/indra/llcommon/stdenums.h
@@ -49,7 +49,8 @@ enum EDragAndDropType
 	DAD_ANIMATION		= 12,
 	DAD_GESTURE			= 13,
 	DAD_LINK			= 14,
-	DAD_COUNT			= 15,   // number of types in this enum
+	DAD_MESH           		= 15,
+	DAD_COUNT			= 16,   // number of types in this enum
 };
 
 // Reasons for drags to be denied.
diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt
index 6b2b61f88353727c667be8711474ac1bc29c9491..35a764b111bf594fedee308be5e6469f4701f199 100644
--- a/indra/llinventory/CMakeLists.txt
+++ b/indra/llinventory/CMakeLists.txt
@@ -59,16 +59,17 @@ list(APPEND llinventory_SOURCE_FILES ${llinventory_HEADER_FILES})
 add_library (llinventory ${llinventory_SOURCE_FILES})
 
 
-if(LL_TESTS)
-  #add unit tests
-  INCLUDE(LLAddBuildTest)
-  SET(llinventory_TEST_SOURCE_FILES
-      # no real unit tests yet!
-      )
-  LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}")
 
-  #set(TEST_DEBUG on)
-  set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
-  LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}")
-  LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}")
-endif(LL_TESTS)
+#add unit tests
+if (LL_TESTS)
+	INCLUDE(LLAddBuildTest)
+	SET(llinventory_TEST_SOURCE_FILES
+	  # no real unit tests yet!
+	  )
+	LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}")
+
+	#set(TEST_DEBUG on)
+	set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+	LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}")
+	LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}")
+endif (LL_TESTS)
diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp
index a99be1420b117d2e6715c591c6ea368222e6441e..d2bba216483796d5533f294d77e71feac32b0495 100644
--- a/indra/llinventory/llinventorytype.cpp
+++ b/indra/llinventory/llinventorytype.cpp
@@ -83,6 +83,7 @@ LLInventoryDictionary::LLInventoryDictionary()
 	addEntry(LLInventoryType::IT_WEARABLE,            new InventoryEntry("wearable",  "wearable",      2, LLAssetType::AT_CLOTHING, LLAssetType::AT_BODYPART));
 	addEntry(LLInventoryType::IT_ANIMATION,           new InventoryEntry("animation", "animation",     1, LLAssetType::AT_ANIMATION));  
 	addEntry(LLInventoryType::IT_GESTURE,             new InventoryEntry("gesture",   "gesture",       1, LLAssetType::AT_GESTURE)); 
+	addEntry(LLInventoryType::IT_MESH,                new InventoryEntry("mesh",      "mesh",          1, LLAssetType::AT_MESH));
 }
 
 
@@ -91,32 +92,58 @@ LLInventoryDictionary::LLInventoryDictionary()
 static const LLInventoryType::EType
 DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] =
 {
-	LLInventoryType::IT_TEXTURE,		// AT_TEXTURE
-	LLInventoryType::IT_SOUND,			// AT_SOUND
-	LLInventoryType::IT_CALLINGCARD,	// AT_CALLINGCARD
-	LLInventoryType::IT_LANDMARK,		// AT_LANDMARK
-	LLInventoryType::IT_LSL,			// AT_SCRIPT
-	LLInventoryType::IT_WEARABLE,		// AT_CLOTHING
-	LLInventoryType::IT_OBJECT,			// AT_OBJECT
-	LLInventoryType::IT_NOTECARD,		// AT_NOTECARD
-	LLInventoryType::IT_CATEGORY,		// AT_CATEGORY
-	LLInventoryType::IT_NONE,			// (null entry)
-	LLInventoryType::IT_LSL,			// AT_LSL_TEXT
-	LLInventoryType::IT_LSL,			// AT_LSL_BYTECODE
-	LLInventoryType::IT_TEXTURE,		// AT_TEXTURE_TGA
-	LLInventoryType::IT_WEARABLE,		// AT_BODYPART
-	LLInventoryType::IT_CATEGORY,		// AT_TRASH
-	LLInventoryType::IT_CATEGORY,		// AT_SNAPSHOT_CATEGORY
-	LLInventoryType::IT_CATEGORY,		// AT_LOST_AND_FOUND
-	LLInventoryType::IT_SOUND,			// AT_SOUND_WAV
-	LLInventoryType::IT_NONE,			// AT_IMAGE_TGA
-	LLInventoryType::IT_NONE,			// AT_IMAGE_JPEG
-	LLInventoryType::IT_ANIMATION,		// AT_ANIMATION
-	LLInventoryType::IT_GESTURE,		// AT_GESTURE
-	LLInventoryType::IT_NONE,			// AT_SIMSTATE
-
-	LLInventoryType::IT_NONE,			// AT_LINK
-	LLInventoryType::IT_NONE,			// AT_LINK_FOLDER
+	LLInventoryType::IT_TEXTURE,		// 0	AT_TEXTURE
+	LLInventoryType::IT_SOUND,			// 1	AT_SOUND
+	LLInventoryType::IT_CALLINGCARD,	// 2	AT_CALLINGCARD
+	LLInventoryType::IT_LANDMARK,		// 3	AT_LANDMARK
+	LLInventoryType::IT_LSL,			// 4	AT_SCRIPT
+	LLInventoryType::IT_WEARABLE,		// 5	AT_CLOTHING
+	LLInventoryType::IT_OBJECT,			// 6	AT_OBJECT
+	LLInventoryType::IT_NOTECARD,		// 7	AT_NOTECARD
+	LLInventoryType::IT_CATEGORY,		// 8	AT_CATEGORY
+	LLInventoryType::IT_NONE,			// 9	(null entry)
+	LLInventoryType::IT_LSL,			// 10	AT_LSL_TEXT
+	LLInventoryType::IT_LSL,			// 11	AT_LSL_BYTECODE
+	LLInventoryType::IT_TEXTURE,		// 12	AT_TEXTURE_TGA
+	LLInventoryType::IT_WEARABLE,		// 13	AT_BODYPART
+	LLInventoryType::IT_CATEGORY,		// 14	AT_TRASH
+	LLInventoryType::IT_CATEGORY,		// 15	AT_SNAPSHOT_CATEGORY
+	LLInventoryType::IT_CATEGORY,		// 16	AT_LOST_AND_FOUND
+	LLInventoryType::IT_SOUND,			// 17	AT_SOUND_WAV
+	LLInventoryType::IT_NONE,			// 18	AT_IMAGE_TGA
+	LLInventoryType::IT_NONE,			// 19	AT_IMAGE_JPEG
+	LLInventoryType::IT_ANIMATION,		// 20	AT_ANIMATION
+	LLInventoryType::IT_GESTURE,		// 21	AT_GESTURE
+	LLInventoryType::IT_NONE,			// 22	AT_SIMSTATE
+
+	LLInventoryType::IT_NONE,			// 23	AT_LINK
+	LLInventoryType::IT_NONE,			// 24	AT_LINK_FOLDER
+
+	LLInventoryType::IT_NONE,			// 25	AT_NONE
+	LLInventoryType::IT_NONE,			// 26	AT_NONE
+	LLInventoryType::IT_NONE,			// 27	AT_NONE
+	LLInventoryType::IT_NONE,			// 28	AT_NONE
+	LLInventoryType::IT_NONE,			// 29	AT_NONE
+	LLInventoryType::IT_NONE,			// 30	AT_NONE
+	LLInventoryType::IT_NONE,			// 31	AT_NONE
+	LLInventoryType::IT_NONE,			// 32	AT_NONE
+	LLInventoryType::IT_NONE,			// 33	AT_NONE
+	LLInventoryType::IT_NONE,			// 34	AT_NONE
+	LLInventoryType::IT_NONE,			// 35	AT_NONE
+	LLInventoryType::IT_NONE,			// 36	AT_NONE
+	LLInventoryType::IT_NONE,			// 37	AT_NONE
+	LLInventoryType::IT_NONE,			// 38	AT_NONE
+	LLInventoryType::IT_NONE,			// 39	AT_NONE
+	LLInventoryType::IT_NONE,			// 40	AT_NONE
+	LLInventoryType::IT_NONE,			// 41	AT_NONE
+	LLInventoryType::IT_NONE,			// 42	AT_NONE
+	LLInventoryType::IT_NONE,			// 43	AT_NONE
+	LLInventoryType::IT_NONE,			// 44	AT_NONE
+	LLInventoryType::IT_NONE,			// 45	AT_NONE
+	LLInventoryType::IT_NONE,			// 46	AT_NONE
+	LLInventoryType::IT_NONE,			// 47	AT_NONE
+	LLInventoryType::IT_NONE,			// 48	AT_NONE
+	LLInventoryType::IT_MESH            // 49	AT_MESH
 };
 
 // static
diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h
index d9777a73f265c53d061f0d1411dac5b63cbf5d3d..1a24e351adc6e5c7c00abc5b89307f4cf418bf0c 100644
--- a/indra/llinventory/llinventorytype.h
+++ b/indra/llinventory/llinventorytype.h
@@ -61,7 +61,8 @@ class LLInventoryType
 		IT_WEARABLE = 18,
 		IT_ANIMATION = 19,
 		IT_GESTURE = 20,
-		IT_COUNT = 21,
+		IT_MESH = 22,
+		IT_COUNT = 23,
 
 		IT_NONE = -1
 	};
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index e93fe906505cb9e406b5db16457c969f019d392d..9dadad7dd34836f0f107afaed854973ddb85d7cd 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -15,13 +15,16 @@ set(llmath_SOURCE_FILES
     llcamera.cpp
     llcoordframe.cpp
     llline.cpp
+    llmatrix3a.cpp
     llmodularmath.cpp
     llperlin.cpp
     llquaternion.cpp
     llrect.cpp
     llsphere.cpp
+    llvector4a.cpp
     llvolume.cpp
     llvolumemgr.cpp
+    llvolumeoctree.cpp
     llsdutil_math.cpp
     m3math.cpp
     m4math.cpp
@@ -49,21 +52,32 @@ set(llmath_HEADER_FILES
     llinterp.h
     llline.h
     llmath.h
+    llmatrix3a.h
+    llmatrix3a.inl
     llmodularmath.h
     lloctree.h
     llperlin.h
     llplane.h
     llquantize.h
     llquaternion.h
+    llquaternion2.h
+    llquaternion2.inl
     llrect.h
+    llsimdmath.h
+    llsimdtypes.h
+    llsimdtypes.inl
     llsphere.h
     lltreenode.h
+    llvector4a.h
+    llvector4a.inl
+    llvector4logical.h
     llv4math.h
     llv4matrix3.h
     llv4matrix4.h
     llv4vector3.h
     llvolume.h
     llvolumemgr.h
+    llvolumeoctree.h
     llsdutil_math.h
     m3math.h
     m4math.h
diff --git a/indra/llmath/llcamera.cpp b/indra/llmath/llcamera.cpp
index 687c1a7d458458049219db147967205452018f21..22ba26f99b20d178f22fae7c5e4c5589470700e5 100644
--- a/indra/llmath/llcamera.cpp
+++ b/indra/llmath/llcamera.cpp
@@ -45,7 +45,6 @@ LLCamera::LLCamera() :
 	calculateFrustumPlanes();
 } 
 
-
 LLCamera::LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) :
 	LLCoordFrame(),
 	mViewHeightInPixels(view_height_in_pixels),
@@ -61,6 +60,10 @@ LLCamera::LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_p
 	setView(vertical_fov_rads);
 } 
 
+LLCamera::~LLCamera()
+{
+
+}
 
 // ---------------- LLCamera::getFoo() member functions ----------------
 
@@ -82,11 +85,11 @@ F32 LLCamera::getMaxView() const
 
 // ---------------- LLCamera::setFoo() member functions ----------------
 
-void LLCamera::setUserClipPlane(LLPlane plane)
+void LLCamera::setUserClipPlane(LLPlane& plane)
 {
 	mPlaneCount = 7;
-	mAgentPlanes[6].p = plane;
-	mAgentPlanes[6].mask = calcPlaneMask(plane);
+	mAgentPlanes[6] = plane;
+	mPlaneMask[6] = plane.calcPlaneMask();
 }
 
 void LLCamera::disableUserClipPlane()
@@ -158,166 +161,91 @@ size_t LLCamera::readFrustumFromBuffer(const char *buffer)
 
 // ---------------- test methods  ---------------- 
 
-S32 LLCamera::AABBInFrustum(const LLVector3 &center, const LLVector3& radius) 
+S32 LLCamera::AABBInFrustum(const LLVector4a &center, const LLVector4a& radius) 
 {
-	static const LLVector3 scaler[] = {
-		LLVector3(-1,-1,-1),
-		LLVector3( 1,-1,-1),
-		LLVector3(-1, 1,-1),
-		LLVector3( 1, 1,-1),
-		LLVector3(-1,-1, 1),
-		LLVector3( 1,-1, 1),
-		LLVector3(-1, 1, 1),
-		LLVector3( 1, 1, 1)
+	static const LLVector4a scaler[] = {
+		LLVector4a(-1,-1,-1),
+		LLVector4a( 1,-1,-1),
+		LLVector4a(-1, 1,-1),
+		LLVector4a( 1, 1,-1),
+		LLVector4a(-1,-1, 1),
+		LLVector4a( 1,-1, 1),
+		LLVector4a(-1, 1, 1),
+		LLVector4a( 1, 1, 1)
 	};
 
 	U8 mask = 0;
-	S32 result = 2;
-
-	/*if (mFrustumCornerDist > 0.f && radius.magVecSquared() > mFrustumCornerDist * mFrustumCornerDist)
-	{ //box is larger than frustum, check frustum quads against box planes
-
-		static const LLVector3 dir[] = 
-		{
-			LLVector3(1, 0, 0),
-			LLVector3(-1, 0, 0),
-			LLVector3(0, 1, 0),
-			LLVector3(0, -1, 0),
-			LLVector3(0, 0, 1),
-			LLVector3(0, 0, -1)
-		};
-
-		U32 quads[] = 
-		{
-			0, 1, 2, 3,
-			0, 1, 5, 4,
-			2, 3, 7, 6,
-			3, 0, 7, 4,
-			1, 2, 6, 4,
-			4, 5, 6, 7
-		};
-
-		result = 0;
-
-		BOOL total_inside = TRUE;
-		for (U32 i = 0; i < 6; i++)
-		{ 
-			LLVector3 p = center + radius.scaledVec(dir[i]);
-			F32 d = -p*dir[i];
-
-			for (U32 j = 0; j <	6; j++)
-			{ //for each quad
-				F32 dist = mAgentFrustum[quads[j*4+0]]*dir[i] + d;
-				if (dist > 0)
-				{ //at least one frustum point is outside the AABB
-					total_inside = FALSE;
-					for (U32 k = 1; k < 4; k++)
-					{ //for each other point on quad
-						if ( mAgentFrustum[quads[j*4+k]]*dir[i]+d  <= 0.f)
-						{ //quad is straddling some plane of AABB
-							return 1;
-						}
-					}
-				}
-				else
-				{
-					for (U32 k = 1; k < 4; k++)
-					{
-						if (mAgentFrustum[quads[j*4+k]]*dir[i]+d > 0.f)
-						{
-							return 1;
-						}
-					}
-				}
-			}
-		}
-
-		if (total_inside)
-		{
-			result = 1;
-		}
-	}
-	else*/
+	bool result = false;
+	LLVector4a rscale, maxp, minp;
+	LLSimdScalar d;
+	for (U32 i = 0; i < mPlaneCount; i++)
 	{
-		for (U32 i = 0; i < mPlaneCount; i++)
+		mask = mPlaneMask[i];
+		if (mask != 0xff)
 		{
-			mask = mAgentPlanes[i].mask;
-			if (mask == 0xff)
-			{
-				continue;
-			}
-			LLPlane p = mAgentPlanes[i].p;
-			LLVector3 n = LLVector3(p);
-			float d = p.mV[3];
-			LLVector3 rscale = radius.scaledVec(scaler[mask]);
-
-			LLVector3 minp = center - rscale;
-			LLVector3 maxp = center + rscale;
-
-			if (n * minp > -d) 
+			const LLPlane& p(mAgentPlanes[i]);
+			p.getAt<3>(d);
+			rscale.setMul(radius, scaler[mask]);
+			minp.setSub(center, rscale);
+			d = -d;
+			if (p.dot3(minp).getF32() > d) 
 			{
 				return 0;
 			}
-		
-			if (n * maxp > -d)
+			
+			if(!result)
 			{
-				result = 1;
+				maxp.setAdd(center, rscale);
+				result = (p.dot3(maxp).getF32() > d);
 			}
 		}
 	}
 
-	
-	return result;
+	return result?1:2;
 }
 
-S32 LLCamera::AABBInFrustumNoFarClip(const LLVector3 &center, const LLVector3& radius) 
+
+S32 LLCamera::AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius) 
 {
-	static const LLVector3 scaler[] = {
-		LLVector3(-1,-1,-1),
-		LLVector3( 1,-1,-1),
-		LLVector3(-1, 1,-1),
-		LLVector3( 1, 1,-1),
-		LLVector3(-1,-1, 1),
-		LLVector3( 1,-1, 1),
-		LLVector3(-1, 1, 1),
-		LLVector3( 1, 1, 1)
+	static const LLVector4a scaler[] = {
+		LLVector4a(-1,-1,-1),
+		LLVector4a( 1,-1,-1),
+		LLVector4a(-1, 1,-1),
+		LLVector4a( 1, 1,-1),
+		LLVector4a(-1,-1, 1),
+		LLVector4a( 1,-1, 1),
+		LLVector4a(-1, 1, 1),
+		LLVector4a( 1, 1, 1)
 	};
 
 	U8 mask = 0;
-	S32 result = 2;
-
+	bool result = false;
+	LLVector4a rscale, maxp, minp;
+	LLSimdScalar d;
 	for (U32 i = 0; i < mPlaneCount; i++)
 	{
-		if (i == 5)
-		{
-			continue;
-		}
-
-		mask = mAgentPlanes[i].mask;
-		if (mask == 0xff)
-		{
-			continue;
-		}
-		LLPlane p = mAgentPlanes[i].p;
-		LLVector3 n = LLVector3(p);
-		float d = p.mV[3];
-		LLVector3 rscale = radius.scaledVec(scaler[mask]);
-
-		LLVector3 minp = center - rscale;
-		LLVector3 maxp = center + rscale;
-
-		if (n * minp > -d) 
+		mask = mPlaneMask[i];
+		if ((i != 5) && (mask != 0xff))
 		{
-			return 0;
-		}
-	
-		if (n * maxp > -d)
-		{
-			result = 1;
+			const LLPlane& p(mAgentPlanes[i]);
+			p.getAt<3>(d);
+			rscale.setMul(radius, scaler[mask]);
+			minp.setSub(center, rscale);
+			d = -d;
+			if (p.dot3(minp).getF32() > d) 
+			{
+				return 0;
+			}
+			
+			if(!result)
+			{
+				maxp.setAdd(center, rscale);
+				result = (p.dot3(maxp).getF32() > d);
+			}
 		}
 	}
 
-	return result;
+	return result?1:2;
 }
 
 int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius) 
@@ -438,28 +366,22 @@ int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radiu
 int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const 
 {
 	// Returns 1 if sphere is in frustum, 0 if not.
-	int res = 2;
+	bool res = false;
 	for (int i = 0; i < 6; i++)
 	{
-		if (mAgentPlanes[i].mask == 0xff)
+		if (mPlaneMask[i] != 0xff)
 		{
-			continue;
-		}
-
-		float d = mAgentPlanes[i].p.dist(sphere_center);
+			float d = mAgentPlanes[i].dist(sphere_center);
 
-		if (d > radius) 
-		{
-			return 0;
-		}
-
-		if (d > -radius)
-		{
-			res = 1;
+			if (d > radius) 
+			{
+				return 0;
+			}
+			res = res || (d > -radius);
 		}
 	}
 
-	return res;
+	return res?1:2;
 }
 
 
@@ -611,25 +533,6 @@ LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3)
 	return LLPlane(p1, n);
 }
 
-U8 LLCamera::calcPlaneMask(const LLPlane& plane)
-{
-	U8 mask = 0;
-	
-	if (plane.mV[0] >= 0)
-	{
-		mask |= 1;
-	}
-	if (plane.mV[1] >= 0)
-	{
-		mask |= 2;
-	}
-	if (plane.mV[2] >= 0)
-	{
-		mask |= 4;
-	}
-
-	return mask;
-}
 
 void LLCamera::ignoreAgentFrustumPlane(S32 idx)
 {
@@ -638,12 +541,13 @@ void LLCamera::ignoreAgentFrustumPlane(S32 idx)
 		return;
 	}
 
-	mAgentPlanes[idx].mask = 0xff;
-	mAgentPlanes[idx].p.clearVec();
+	mPlaneMask[idx] = 0xff;
+	mAgentPlanes[idx].clear();
 }
 
 void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
 {
+	
 	for (int i = 0; i < 8; i++)
 	{
 		mAgentFrustum[i] = frust[i];
@@ -656,27 +560,27 @@ void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
 	//order of planes is important, keep most likely to fail in the front of the list
 
 	//near - frust[0], frust[1], frust[2]
-	mAgentPlanes[2].p = planeFromPoints(frust[0], frust[1], frust[2]);
+	mAgentPlanes[2] = planeFromPoints(frust[0], frust[1], frust[2]);
 
 	//far  
-	mAgentPlanes[5].p = planeFromPoints(frust[5], frust[4], frust[6]);
+	mAgentPlanes[5] = planeFromPoints(frust[5], frust[4], frust[6]);
 
 	//left  
-	mAgentPlanes[0].p = planeFromPoints(frust[4], frust[0], frust[7]);
+	mAgentPlanes[0] = planeFromPoints(frust[4], frust[0], frust[7]);
 
 	//right  
-	mAgentPlanes[1].p = planeFromPoints(frust[1], frust[5], frust[6]);
+	mAgentPlanes[1] = planeFromPoints(frust[1], frust[5], frust[6]);
 
 	//top  
-	mAgentPlanes[4].p = planeFromPoints(frust[3], frust[2], frust[6]);
+	mAgentPlanes[4] = planeFromPoints(frust[3], frust[2], frust[6]);
 
 	//bottom  
-	mAgentPlanes[3].p = planeFromPoints(frust[1], frust[0], frust[4]);
+	mAgentPlanes[3] = planeFromPoints(frust[1], frust[0], frust[4]);
 
 	//cache plane octant facing mask for use in AABBInFrustum
 	for (U32 i = 0; i < mPlaneCount; i++)
 	{
-		mAgentPlanes[i].mask = calcPlaneMask(mAgentPlanes[i].p);
+		mPlaneMask[i] = mAgentPlanes[i].calcPlaneMask();
 	}
 }
 
@@ -730,9 +634,10 @@ void LLCamera::calculateWorldFrustumPlanes()
 	F32 d;
 	LLVector3 center = mOrigin - mXAxis*mNearPlane;
 	mWorldPlanePos = center;
+	LLVector3 pnorm;	
 	for (int p=0; p<4; p++)
 	{
-		LLVector3 pnorm = LLVector3(mLocalPlanes[p]);
+		mLocalPlanes[p].getVector3(pnorm);
 		LLVector3 norm = rotateToAbsolute(pnorm);
 		norm.normVec();
 		d = -(center * norm);
@@ -742,13 +647,15 @@ void LLCamera::calculateWorldFrustumPlanes()
 	LLVector3 zaxis(0, 0, 1.0f);
 	F32 yaw = getYaw();
 	{
-		LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_LEFT]);
+		LLVector3 tnorm;
+		mLocalPlanes[PLANE_LEFT].getVector3(tnorm);
 		tnorm.rotVec(yaw, zaxis);
 		d = -(mOrigin * tnorm);
 		mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
 	}
 	{
-		LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_RIGHT]);
+		LLVector3 tnorm;
+		mLocalPlanes[PLANE_RIGHT].getVector3(tnorm);
 		tnorm.rotVec(yaw, zaxis);
 		d = -(mOrigin * tnorm);
 		mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h
index 531144db39053bc6adf41410faa4cf2b39559957..ec67b91d05f0c14f7f5ad7f3c062a29e542e0143 100644
--- a/indra/llmath/llcamera.h
+++ b/indra/llmath/llcamera.h
@@ -31,6 +31,7 @@
 #include "llmath.h"
 #include "llcoordframe.h"
 #include "llplane.h"
+#include "llvector4a.h"
 
 const F32 DEFAULT_FIELD_OF_VIEW 	= 60.f * DEG_TO_RAD;
 const F32 DEFAULT_ASPECT_RATIO 		= 640.f / 480.f;
@@ -64,6 +65,12 @@ class LLCamera
 : 	public LLCoordFrame
 {
 public:
+	
+	LLCamera(const LLCamera& rhs)
+	{
+		*this = rhs;
+	}
+	
 	enum {
 		PLANE_LEFT = 0,
 		PLANE_RIGHT = 1,
@@ -101,6 +108,9 @@ class LLCamera
 	};
 
 private:
+	LLPlane mAgentPlanes[7];  //frustum planes in agent space a la gluUnproject (I'm a bastard, I know) - DaveP
+	U8 mPlaneMask[8];         // 8 for alignment	
+	
 	F32 mView;					// angle between top and bottom frustum planes in radians.
 	F32 mAspect;				// width/height
 	S32 mViewHeightInPixels;	// for ViewHeightInPixels() only
@@ -114,30 +124,22 @@ class LLCamera
 	LLPlane mWorldPlanes[PLANE_NUM];
 	LLPlane mHorizPlanes[HORIZ_PLANE_NUM];
 
-	struct frustum_plane
-	{
-		frustum_plane() : mask(0) {}
-		LLPlane p;
-		U8 mask;
-	};
-	frustum_plane mAgentPlanes[7];  //frustum planes in agent space a la gluUnproject (I'm a bastard, I know) - DaveP
-
 	U32 mPlaneCount;  //defaults to 6, if setUserClipPlane is called, uses user supplied clip plane in
 
 	LLVector3 mWorldPlanePos;		// Position of World Planes (may be offset from camera)
 public:
 	LLVector3 mAgentFrustum[8];  //8 corners of 6-plane frustum
 	F32	mFrustumCornerDist;		//distance to corner of frustum against far clip plane
-	LLPlane getAgentPlane(U32 idx) { return mAgentPlanes[idx].p; }
+	LLPlane& getAgentPlane(U32 idx) { return mAgentPlanes[idx]; }
 
 public:
 	LLCamera();
 	LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane);
-	virtual ~LLCamera(){} // no-op virtual destructor
+	virtual ~LLCamera();
+	
 
-	void setUserClipPlane(LLPlane plane);
+	void setUserClipPlane(LLPlane& plane);
 	void disableUserClipPlane();
-	U8 calcPlaneMask(const LLPlane& plane);
 	virtual void setView(F32 vertical_fov_rads);
 	void setViewHeightInPixels(S32 height);
 	void setAspect(F32 new_aspect);
@@ -184,8 +186,8 @@ class LLCamera
 	S32 sphereInFrustum(const LLVector3 &center, const F32 radius) const;
 	S32 pointInFrustum(const LLVector3 &point) const { return sphereInFrustum(point, 0.0f); }
 	S32 sphereInFrustumFull(const LLVector3 &center, const F32 radius) const { return sphereInFrustum(center, radius); }
-	S32 AABBInFrustum(const LLVector3 &center, const LLVector3& radius);
-	S32 AABBInFrustumNoFarClip(const LLVector3 &center, const LLVector3& radius);
+	S32 AABBInFrustum(const LLVector4a& center, const LLVector4a& radius);
+	S32 AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius);
 
 	//does a quick 'n dirty sphere-sphere check
 	S32 sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius); 
diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h
index 798f1154d0230cb297f3b476c20a6f3b2fa715d9..eea7c977fb9595222965fece31f14d586cd87829 100644
--- a/indra/llmath/llmath.h
+++ b/indra/llmath/llmath.h
@@ -29,7 +29,7 @@
 
 #include <cmath>
 #include <cstdlib>
-#include <complex>
+#include <vector>
 #include "lldefs.h"
 //#include "llstl.h" // *TODO: Remove when LLString is gone
 //#include "llstring.h" // *TODO: Remove when LLString is gone
@@ -55,32 +55,11 @@
 #endif
 
 // Single Precision Floating Point Routines
-#ifndef sqrtf
-#define sqrtf(x)	((F32)sqrt((F64)(x)))
-#endif
-#ifndef fsqrtf
-#define fsqrtf(x)	sqrtf(x)
-#endif
-
-#ifndef cosf
-#define cosf(x)		((F32)cos((F64)(x)))
-#endif
-#ifndef sinf
-#define sinf(x)		((F32)sin((F64)(x)))
-#endif
-#ifndef tanf
+// (There used to be more defined here, but they appeared to be redundant and 
+// were breaking some other includes. Removed by Falcon, reviewed by Andrew, 11/25/09)
+/*#ifndef tanf
 #define tanf(x)		((F32)tan((F64)(x)))
-#endif
-#ifndef acosf
-#define acosf(x)	((F32)acos((F64)(x)))
-#endif
-
-#ifndef powf
-#define powf(x,y)	((F32)pow((F64)(x),(F64)(y)))
-#endif
-#ifndef expf
-#define expf(x)		((F32)exp((F64)(x)))
-#endif
+#endif*/
 
 const F32	GRAVITY			= -9.8f;
 
@@ -200,7 +179,7 @@ inline S32 llfloor( F32 f )
 		}
 		return result;
 #else
-		return (S32)floorf(f);
+		return (S32)floor(f);
 #endif
 }
 
@@ -378,11 +357,14 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs)
 		bar *= 10.f;
 	}
 
-	foo = (F32)llround(foo * bar);
+	//F32 new_foo = (F32)llround(foo * bar);
+	// the llround() implementation sucks.  Don't us it.
 
-	// shift back
-	foo /= bar;
-	return foo;
+	F32 sign = (foo > 0.f) ? 1.f : -1.f;
+	F32 new_foo = F32( S64(foo * bar + sign * 0.5f));
+	new_foo /= bar;
+
+	return new_foo;
 }
 
 inline F32 lerp(F32 a, F32 b, F32 u) 
@@ -516,4 +498,45 @@ inline F32 llgaussian(F32 x, F32 o)
 	return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2*o*o));
 }
 
+//helper function for removing outliers
+template <class VEC_TYPE>
+inline void ll_remove_outliers(std::vector<VEC_TYPE>& data, F32 k)
+{
+	if (data.size() < 100)
+	{ //not enough samples
+		return;
+	}
+
+	VEC_TYPE Q1 = data[data.size()/4];
+	VEC_TYPE Q3 = data[data.size()-data.size()/4-1];
+
+	VEC_TYPE min = (VEC_TYPE) ((F32) Q1-k * (F32) (Q3-Q1));
+	VEC_TYPE max = (VEC_TYPE) ((F32) Q3+k * (F32) (Q3-Q1));
+
+	U32 i = 0;
+	while (i < data.size() && data[i] < min)
+	{
+		i++;
+	}
+
+	S32 j = data.size()-1;
+	while (j > 0 && data[j] > max)
+	{
+		j--;
+	}
+
+	if (j < data.size()-1)
+	{
+		data.erase(data.begin()+j, data.end());
+	}
+
+	if (i > 0)
+	{
+		data.erase(data.begin(), data.begin()+i);
+	}
+}
+
+// Include simd math header
+#include "llsimdmath.h"
+
 #endif
diff --git a/indra/llmath/llmatrix3a.cpp b/indra/llmath/llmatrix3a.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab077abcb08d2564d9182a431705847f5d9d2f71
--- /dev/null
+++ b/indra/llmath/llmatrix3a.cpp
@@ -0,0 +1,134 @@
+/** 
+ * @file llvector4a.cpp
+ * @brief SIMD vector implementation
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llmath.h"
+
+static LL_ALIGN_16(const F32 M_IDENT_3A[12]) = 
+												{	1.f, 0.f, 0.f, 0.f, // Column 1
+													0.f, 1.f, 0.f, 0.f, // Column 2
+													0.f, 0.f, 1.f, 0.f }; // Column 3
+
+extern const LLMatrix3a LL_M3A_IDENTITY = *reinterpret_cast<const LLMatrix3a*> (M_IDENT_3A);
+
+void LLMatrix3a::setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs )
+{
+	const LLVector4a col0 = lhs.getColumn(0);
+	const LLVector4a col1 = lhs.getColumn(1);
+	const LLVector4a col2 = lhs.getColumn(2);
+
+	for ( int i = 0; i < 3; i++ )
+	{
+		LLVector4a xxxx = _mm_load_ss( rhs.mColumns[i].getF32ptr() );
+		xxxx.splat<0>( xxxx );
+		xxxx.mul( col0 );
+
+		{
+			LLVector4a yyyy = _mm_load_ss( rhs.mColumns[i].getF32ptr() +  1 );
+			yyyy.splat<0>( yyyy );
+			yyyy.mul( col1 ); 
+			xxxx.add( yyyy );
+		}
+
+		{
+			LLVector4a zzzz = _mm_load_ss( rhs.mColumns[i].getF32ptr() +  2 );
+			zzzz.splat<0>( zzzz );
+			zzzz.mul( col2 );
+			xxxx.add( zzzz );
+		}
+
+		xxxx.store4a( mColumns[i].getF32ptr() );
+	}
+	
+}
+
+/*static */void LLMatrix3a::batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst )
+{
+	const LLVector4a col0 = xform.getColumn(0);
+	const LLVector4a col1 = xform.getColumn(1);
+	const LLVector4a col2 = xform.getColumn(2);
+	const LLVector4a* maxAddr = src + numVectors;
+
+	if ( numVectors & 0x1 )
+	{
+		LLVector4a xxxx = _mm_load_ss( (const F32*)src );
+		LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 );
+		LLVector4a zzzz = _mm_load_ss( (const F32*)src + 2 );
+		xxxx.splat<0>( xxxx );
+		yyyy.splat<0>( yyyy );
+		zzzz.splat<0>( zzzz );
+		xxxx.mul( col0 );
+		yyyy.mul( col1 ); 
+		zzzz.mul( col2 );
+		xxxx.add( yyyy );
+		xxxx.add( zzzz );
+		xxxx.store4a( (F32*)dst );
+		src++;
+		dst++;
+	}
+
+
+	numVectors >>= 1;
+	while ( src < maxAddr )
+	{
+		_mm_prefetch( (const char*)(src + 32 ), _MM_HINT_NTA );
+		_mm_prefetch( (const char*)(dst + 32), _MM_HINT_NTA );
+		LLVector4a xxxx = _mm_load_ss( (const F32*)src );
+		LLVector4a xxxx1= _mm_load_ss( (const F32*)(src + 1) );
+
+		xxxx.splat<0>( xxxx );
+		xxxx1.splat<0>( xxxx1 );
+		xxxx.mul( col0 );
+		xxxx1.mul( col0 );
+
+		{
+			LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 );
+			LLVector4a yyyy1 = _mm_load_ss( (const F32*)(src + 1) + 1);
+			yyyy.splat<0>( yyyy );
+			yyyy1.splat<0>( yyyy1 );
+			yyyy.mul( col1 );
+			yyyy1.mul( col1 );
+			xxxx.add( yyyy );
+			xxxx1.add( yyyy1 );
+		}
+
+		{
+			LLVector4a zzzz = _mm_load_ss( (const F32*)(src) + 2 );
+			LLVector4a zzzz1 = _mm_load_ss( (const F32*)(++src) + 2 );
+			zzzz.splat<0>( zzzz );
+			zzzz1.splat<0>( zzzz1 );
+			zzzz.mul( col2 );
+			zzzz1.mul( col2 );
+			xxxx.add( zzzz );
+			xxxx1.add( zzzz1 );
+		}
+
+		xxxx.store4a(dst->getF32ptr());
+		src++;
+		dst++;
+
+		xxxx1.store4a((F32*)dst++);
+	}
+}
diff --git a/indra/llmath/llmatrix3a.h b/indra/llmath/llmatrix3a.h
new file mode 100644
index 0000000000000000000000000000000000000000..adb7e3389d5e54e1fa7695ae6700fadad83dcfe2
--- /dev/null
+++ b/indra/llmath/llmatrix3a.h
@@ -0,0 +1,128 @@
+/** 
+ * @file llmatrix3a.h
+ * @brief LLMatrix3a class header file - memory aligned and vectorized 3x3 matrix
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef	LL_LLMATRIX3A_H
+#define	LL_LLMATRIX3A_H
+
+/////////////////////////////
+// LLMatrix3a, LLRotation
+/////////////////////////////
+// This class stores a 3x3 (technically 4x3) matrix in column-major order
+/////////////////////////////
+/////////////////////////////
+// These classes are intentionally minimal right now. If you need additional
+// functionality, please contact someone with SSE experience (e.g., Falcon or
+// Huseby).
+/////////////////////////////
+
+// LLMatrix3a is the base class for LLRotation, which should be used instead any time you're dealing with a 
+// rotation matrix.
+class LLMatrix3a
+{
+public:
+
+	// Utility function for quickly transforming an array of LLVector4a's
+	// For transforming a single LLVector4a, see LLVector4a::setRotated
+	static void batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst );
+
+	// Utility function to obtain the identity matrix
+	static inline const LLMatrix3a& getIdentity();
+
+	//////////////////////////
+	// Ctors
+	//////////////////////////
+	
+	// Ctor
+	LLMatrix3a() {}
+
+	// Ctor for setting by columns
+	inline LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 );
+
+	//////////////////////////
+	// Get/Set
+	//////////////////////////
+
+	// Loads from an LLMatrix3
+	inline void loadu(const LLMatrix3& src);
+	
+	// Set rows
+	inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2);
+	
+	// Set columns
+	inline void setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2);
+
+	// Get the read-only access to a specified column. Valid columns are 0-2, but the 
+	// function is unchecked. You've been warned.
+	inline const LLVector4a& getColumn(const U32 column) const;
+
+	/////////////////////////
+	// Matrix modification
+	/////////////////////////
+	
+	// Set this matrix to the product of lhs and rhs ( this = lhs * rhs )
+	void setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs );
+
+	// Set this matrix to the transpose of src
+	inline void setTranspose(const LLMatrix3a& src);
+
+	// Set this matrix to a*w + b*(1-w)
+	inline void setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w);
+
+	/////////////////////////
+	// Matrix inspection
+	/////////////////////////
+
+	// Sets all 4 elements in 'dest' to the determinant of this matrix.
+	// If you will be using the determinant in subsequent ops with LLVector4a, use this version
+	inline void getDeterminant( LLVector4a& dest ) const;
+
+	// Returns the determinant as an LLSimdScalar. Use this if you will be using the determinant
+	// primary for scalar operations.
+	inline LLSimdScalar getDeterminant() const;
+
+	// Returns nonzero if rows 0-2 and colums 0-2 contain no NaN or INF values. Row 3 is ignored
+	inline LLBool32 isFinite() const;
+
+	// Returns true if this matrix is equal to 'rhs' up to 'tolerance'
+	inline bool isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+protected:
+
+	LLVector4a mColumns[3];
+
+};
+
+class LLRotation : public LLMatrix3a
+{
+public:
+	
+	LLRotation() {}
+	
+	// Returns true if this rotation is orthonormal with det ~= 1
+	inline bool isOkRotation() const;		
+};
+
+#endif
diff --git a/indra/llmath/llmatrix3a.inl b/indra/llmath/llmatrix3a.inl
new file mode 100644
index 0000000000000000000000000000000000000000..37819fea3cfec1fcd1c94c91efb81f815ee84a03
--- /dev/null
+++ b/indra/llmath/llmatrix3a.inl
@@ -0,0 +1,119 @@
+/** 
+ * @file llmatrix3a.inl
+ * @brief LLMatrix3a inline definitions
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llmatrix3a.h"
+#include "m3math.h"
+
+inline LLMatrix3a::LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 )
+{
+	setColumns( c0, c1, c2 );
+}
+
+inline void LLMatrix3a::loadu(const LLMatrix3& src)
+{
+	mColumns[0].load3(src.mMatrix[0]);
+	mColumns[1].load3(src.mMatrix[1]);
+	mColumns[2].load3(src.mMatrix[2]);
+}
+
+inline void LLMatrix3a::setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2)
+{
+	mColumns[0] = r0;
+	mColumns[1] = r1;
+	mColumns[2] = r2;
+	setTranspose( *this );
+}
+
+inline void LLMatrix3a::setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2)
+{
+	mColumns[0] = c0;
+	mColumns[1] = c1;
+	mColumns[2] = c2;
+}
+
+inline void LLMatrix3a::setTranspose(const LLMatrix3a& src)
+{
+	const LLQuad srcCol0 = src.mColumns[0];
+	const LLQuad srcCol1 = src.mColumns[1];
+	const LLQuad unpacklo = _mm_unpacklo_ps( srcCol0, srcCol1 );
+	mColumns[0] = _mm_movelh_ps( unpacklo, src.mColumns[2] );
+	mColumns[1] = _mm_shuffle_ps( _mm_movehl_ps( srcCol0, unpacklo ), src.mColumns[2], _MM_SHUFFLE(0, 1, 1, 0) );
+	mColumns[2] = _mm_shuffle_ps( _mm_unpackhi_ps( srcCol0, srcCol1 ), src.mColumns[2], _MM_SHUFFLE(0, 2, 1, 0) );
+}
+
+inline const LLVector4a& LLMatrix3a::getColumn(const U32 column) const
+{
+	llassert( column < 3 );
+	return mColumns[column];
+}
+
+inline void LLMatrix3a::setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w)
+{
+	mColumns[0].setLerp( a.mColumns[0], b.mColumns[0], w );
+	mColumns[1].setLerp( a.mColumns[1], b.mColumns[1], w );
+	mColumns[2].setLerp( a.mColumns[2], b.mColumns[2], w );
+}
+
+inline LLBool32 LLMatrix3a::isFinite() const
+{
+	return mColumns[0].isFinite3() && mColumns[1].isFinite3() && mColumns[2].isFinite3();
+}
+
+inline void LLMatrix3a::getDeterminant( LLVector4a& dest ) const
+{
+	LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] );
+	dest.setAllDot3( col1xcol2, mColumns[0] );
+}
+
+inline LLSimdScalar LLMatrix3a::getDeterminant() const
+{
+	LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] );
+	return col1xcol2.dot3( mColumns[0] );
+}
+
+inline bool LLMatrix3a::isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance /*= F_APPROXIMATELY_ZERO*/ ) const
+{
+	return rhs.getColumn(0).equals3(mColumns[0], tolerance) 
+		&& rhs.getColumn(1).equals3(mColumns[1], tolerance) 
+		&& rhs.getColumn(2).equals3(mColumns[2], tolerance); 
+}
+
+inline const LLMatrix3a& LLMatrix3a::getIdentity()
+{
+	extern const LLMatrix3a LL_M3A_IDENTITY;
+	return LL_M3A_IDENTITY;
+}
+
+inline bool LLRotation::isOkRotation() const
+{
+	LLMatrix3a transpose; transpose.setTranspose( *this );
+	LLMatrix3a product; product.setMul( *this, transpose );
+
+	LLSimdScalar detMinusOne = getDeterminant() - 1.f;
+
+	return product.isApproximatelyEqual( LLMatrix3a::getIdentity() ) && (detMinusOne.getAbs() < F_APPROXIMATELY_ZERO);
+}
+
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
new file mode 100644
index 0000000000000000000000000000000000000000..27cf5b79f648939809fb0f2ab35b473748138698
--- /dev/null
+++ b/indra/llmath/llmatrix4a.h
@@ -0,0 +1,143 @@
+/** 
+ * @file llmatrix4a.h
+ * @brief LLMatrix4a class header file - memory aligned and vectorized 4x4 matrix
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef	LL_LLMATRIX4A_H
+#define	LL_LLMATRIX4A_H
+
+#include "llvector4a.h"
+#include "m4math.h"
+#include "m3math.h"
+
+class LLMatrix4a
+{
+public:
+	LLVector4a mMatrix[4];
+
+	inline void clear()
+	{
+		mMatrix[0].clear();
+		mMatrix[1].clear();
+		mMatrix[2].clear();
+		mMatrix[3].clear();
+	}
+
+	inline void loadu(const LLMatrix4& src)
+	{
+		mMatrix[0] = _mm_loadu_ps(src.mMatrix[0]);
+		mMatrix[1] = _mm_loadu_ps(src.mMatrix[1]);
+		mMatrix[2] = _mm_loadu_ps(src.mMatrix[2]);
+		mMatrix[3] = _mm_loadu_ps(src.mMatrix[3]);
+		
+	}
+
+	inline void loadu(const LLMatrix3& src)
+	{
+		mMatrix[0].load3(src.mMatrix[0]);
+		mMatrix[1].load3(src.mMatrix[1]);
+		mMatrix[2].load3(src.mMatrix[2]);
+		mMatrix[3].set(0,0,0,1.f);
+	}
+
+	inline void add(const LLMatrix4a& rhs)
+	{
+		mMatrix[0].add(rhs.mMatrix[0]);
+		mMatrix[1].add(rhs.mMatrix[1]);
+		mMatrix[2].add(rhs.mMatrix[2]);
+		mMatrix[3].add(rhs.mMatrix[3]);
+	}
+
+	inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2)
+	{
+		mMatrix[0] = r0;
+		mMatrix[1] = r1;
+		mMatrix[2] = r2;
+	}
+
+	inline void setMul(const LLMatrix4a& m, const F32 s)
+	{
+		mMatrix[0].setMul(m.mMatrix[0], s);
+		mMatrix[1].setMul(m.mMatrix[1], s);
+		mMatrix[2].setMul(m.mMatrix[2], s);
+		mMatrix[3].setMul(m.mMatrix[3], s);
+	}
+
+	inline void setLerp(const LLMatrix4a& a, const LLMatrix4a& b, F32 w)
+	{
+		LLVector4a d0,d1,d2,d3;
+		d0.setSub(b.mMatrix[0], a.mMatrix[0]);
+		d1.setSub(b.mMatrix[1], a.mMatrix[1]);
+		d2.setSub(b.mMatrix[2], a.mMatrix[2]);
+		d3.setSub(b.mMatrix[3], a.mMatrix[3]);
+
+		// this = a + d*w
+		
+		d0.mul(w);
+		d1.mul(w);
+		d2.mul(w);
+		d3.mul(w);
+
+		mMatrix[0].setAdd(a.mMatrix[0],d0);
+		mMatrix[1].setAdd(a.mMatrix[1],d1);
+		mMatrix[2].setAdd(a.mMatrix[2],d2);
+		mMatrix[3].setAdd(a.mMatrix[3],d3);
+	}
+
+	inline void rotate(const LLVector4a& v, LLVector4a& res)
+	{
+		res = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0));
+		res.mul(mMatrix[0]);
+		
+		LLVector4a y;
+		y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1));
+		y.mul(mMatrix[1]);
+
+		LLVector4a z;
+		z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2));
+		z.mul(mMatrix[2]);
+
+		res.add(y);
+		res.add(z);
+	}
+
+	inline void affineTransform(const LLVector4a& v, LLVector4a& res)
+	{
+		LLVector4a x,y,z;
+
+		x = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0));
+		y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1));
+		z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2));
+		
+		x.mul(mMatrix[0]);
+		y.mul(mMatrix[1]);
+		z.mul(mMatrix[2]);
+
+		x.add(y);
+		z.add(mMatrix[3]);
+		res.setAdd(x,z);
+	}
+};
+
+#endif
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
index 90d4d742c92a3fa3426e88a783af66153e93d504..fdfc24f8b7fbe14c676904d914652f4acb03d213 100644
--- a/indra/llmath/lloctree.h
+++ b/indra/llmath/lloctree.h
@@ -29,14 +29,11 @@
 
 #include "lltreenode.h"
 #include "v3math.h"
+#include "llvector4a.h"
 #include <vector>
 #include <set>
 
-#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG
-#define OCT_ERRS LL_ERRS("OctreeErrors")
-#else
 #define OCT_ERRS LL_WARNS("OctreeErrors")
-#endif
 
 #define LL_OCTREE_PARANOIA_CHECK 0
 #if LL_DARWIN
@@ -66,6 +63,13 @@ class LLOctreeTraveler
 	virtual void visit(const LLOctreeNode<T>* branch) = 0;
 };
 
+template <class T>
+class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T>
+{
+public:
+	virtual void traverse(const LLOctreeNode<T>* node);
+};
+
 template <class T>
 class LLOctreeNode : public LLTreeNode<T>
 {
@@ -81,23 +85,30 @@ class LLOctreeNode : public LLTreeNode<T>
 	typedef LLOctreeNode<T>		oct_node;
 	typedef LLOctreeListener<T>	oct_listener;
 
-	static const U8 OCTANT_POSITIVE_X = 0x01;
-	static const U8 OCTANT_POSITIVE_Y = 0x02;
-	static const U8 OCTANT_POSITIVE_Z = 0x04;
-		
-	LLOctreeNode(	LLVector3d center, 
-					LLVector3d size, 
+	/*void* operator new(size_t size)
+	{
+		return ll_aligned_malloc_16(size);
+	}
+
+	void operator delete(void* ptr)
+	{
+		ll_aligned_free_16(ptr);
+	}*/
+
+	LLOctreeNode(	const LLVector4a& center, 
+					const LLVector4a& size, 
 					BaseType* parent, 
 					U8 octant = 255)
 	:	mParent((oct_node*)parent), 
-		mCenter(center), 
-		mSize(size), 
 		mOctant(octant) 
 	{ 
+		mCenter = center;
+		mSize = size;
+
 		updateMinMax();
 		if ((mOctant == 255) && mParent)
 		{
-			mOctant = ((oct_node*) mParent)->getOctant(mCenter.mdV);
+			mOctant = ((oct_node*) mParent)->getOctant(mCenter);
 		}
 
 		clearChildren();
@@ -114,40 +125,24 @@ class LLOctreeNode : public LLTreeNode<T>
 	}
 
 	inline const BaseType* getParent()	const			{ return mParent; }
-	inline void setParent(BaseType* parent)			{ mParent = (oct_node*) parent; }
-	inline const LLVector3d& getCenter() const			{ return mCenter; }
-	inline const LLVector3d& getSize() const			{ return mSize; }
-	inline void setCenter(LLVector3d center)			{ mCenter = center; }
-	inline void setSize(LLVector3d size)				{ mSize = size; }
-    inline oct_node* getNodeAt(T* data)				{ return getNodeAt(data->getPositionGroup(), data->getBinRadius()); }
-	inline U8 getOctant() const						{ return mOctant; }
-	inline void setOctant(U8 octant)					{ mOctant = octant; }
+	inline void setParent(BaseType* parent)				{ mParent = (oct_node*) parent; }
+	inline const LLVector4a& getCenter() const			{ return mCenter; }
+	inline const LLVector4a& getSize() const			{ return mSize; }
+	inline void setCenter(const LLVector4a& center)		{ mCenter = center; }
+	inline void setSize(const LLVector4a& size)			{ mSize = size; }
+    inline oct_node* getNodeAt(T* data)					{ return getNodeAt(data->getPositionGroup(), data->getBinRadius()); }
+	inline U8 getOctant() const							{ return mOctant; }
 	inline const oct_node*	getOctParent() const		{ return (const oct_node*) getParent(); }
 	inline oct_node* getOctParent() 					{ return (oct_node*) getParent(); }
 	
-	U8 getOctant(const F64 pos[]) const	//get the octant pos is in
+	U8 getOctant(const LLVector4a& pos) const			//get the octant pos is in
 	{
-		U8 ret = 0;
-
-		if (pos[0] > mCenter.mdV[0])
-		{
-			ret |= OCTANT_POSITIVE_X;
-		}
-		if (pos[1] > mCenter.mdV[1])
-		{
-			ret |= OCTANT_POSITIVE_Y;
-		}
-		if (pos[2] > mCenter.mdV[2])
-		{
-			ret |= OCTANT_POSITIVE_Z;
-		}
-
-		return ret;
+		return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7);
 	}
 	
-	inline bool isInside(const LLVector3d& pos, const F64& rad) const
+	inline bool isInside(const LLVector4a& pos, const F32& rad) const
 	{
-		return rad <= mSize.mdV[0]*2.0 && isInside(pos); 
+		return rad <= mSize[0]*2.f && isInside(pos); 
 	}
 
 	inline bool isInside(T* data) const			
@@ -155,29 +150,27 @@ class LLOctreeNode : public LLTreeNode<T>
 		return isInside(data->getPositionGroup(), data->getBinRadius());
 	}
 
-	bool isInside(const LLVector3d& pos) const
+	bool isInside(const LLVector4a& pos) const
 	{
-		const F64& x = pos.mdV[0];
-		const F64& y = pos.mdV[1];
-		const F64& z = pos.mdV[2];
-			
-		if (x > mMax.mdV[0] || x <= mMin.mdV[0] ||
-			y > mMax.mdV[1] || y <= mMin.mdV[1] ||
-			z > mMax.mdV[2] || z <= mMin.mdV[2])
+		S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7;
+		if (gt)
 		{
 			return false;
 		}
-		
+
+		S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7;
+		if (lt)
+		{
+			return false;
+		}
+				
 		return true;
 	}
 	
 	void updateMinMax()
 	{
-		for (U32 i = 0; i < 3; i++)
-		{
-			mMax.mdV[i] = mCenter.mdV[i] + mSize.mdV[i];
-			mMin.mdV[i] = mCenter.mdV[i] - mSize.mdV[i];
-		}
+		mMax.setAdd(mCenter, mSize);
+		mMin.setSub(mCenter, mSize);
 	}
 
 	inline oct_listener* getOctListener(U32 index) 
@@ -190,34 +183,34 @@ class LLOctreeNode : public LLTreeNode<T>
 		return contains(xform->getBinRadius());
 	}
 
-	bool contains(F64 radius)
+	bool contains(F32 radius)
 	{
 		if (mParent == NULL)
 		{	//root node contains nothing
 			return false;
 		}
 
-		F64 size = mSize.mdV[0];
-		F64 p_size = size * 2.0;
+		F32 size = mSize[0];
+		F32 p_size = size * 2.f;
 
-		return (radius <= 0.001 && size <= 0.001) ||
+		return (radius <= 0.001f && size <= 0.001f) ||
 				(radius <= p_size && radius > size);
 	}
 
-	static void pushCenter(LLVector3d &center, const LLVector3d &size, const T* data)
+	static void pushCenter(LLVector4a &center, const LLVector4a &size, const T* data)
 	{
-		const LLVector3d& pos = data->getPositionGroup();
-		for (U32 i = 0; i < 3; i++)
-		{
-			if (pos.mdV[i] > center.mdV[i])
-			{
-				center.mdV[i] += size.mdV[i];
-			}
-			else 
-			{
-				center.mdV[i] -= size.mdV[i];
-			}
-		}
+		const LLVector4a& pos = data->getPositionGroup();
+
+		LLVector4Logical gt = pos.greaterThan(center);
+
+		LLVector4a up;
+		up = _mm_and_ps(size, gt);
+
+		LLVector4a down;
+		down = _mm_andnot_ps(gt, size);
+
+		center.add(up);
+		center.sub(down);
 	}
 
 	void accept(oct_traveler* visitor)				{ visitor->visit(this); }
@@ -236,32 +229,49 @@ class LLOctreeNode : public LLTreeNode<T>
 	void accept(tree_traveler* visitor) const		{ visitor->visit(this); }
 	void accept(oct_traveler* visitor) const		{ visitor->visit(this); }
 	
-	oct_node* getNodeAt(const LLVector3d& pos, const F64& rad)
+	void validateChildMap()
+	{
+		for (U32 i = 0; i < 8; i++)
+		{
+			U8 idx = mChildMap[i];
+			if (idx != 255)
+			{
+				LLOctreeNode<T>* child = mChild[idx];
+
+				if (child->getOctant() != i)
+				{
+					llerrs << "Invalid child map, bad octant data." << llendl;
+				}
+
+				if (getOctant(child->getCenter()) != child->getOctant())
+				{
+					llerrs << "Invalid child octant compared to position data." << llendl;
+				}
+			}
+		}
+	}
+
+
+	oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
 	{ 
 		LLOctreeNode<T>* node = this;
 
 		if (node->isInside(pos, rad))
 		{		
 			//do a quick search by octant
-			U8 octant = node->getOctant(pos.mdV);
-			BOOL keep_going = TRUE;
-
+			U8 octant = node->getOctant(pos);
+			
 			//traverse the tree until we find a node that has no node
 			//at the appropriate octant or is smaller than the object.  
 			//by definition, that node is the smallest node that contains 
 			// the data
-			while (keep_going && node->getSize().mdV[0] >= rad)
+			U8 next_node = node->mChildMap[octant];
+			
+			while (next_node != 255 && node->getSize()[0] >= rad)
 			{	
-				keep_going = FALSE;
-				for (U32 i = 0; i < node->getChildCount() && !keep_going; i++)
-				{
-					if (node->getChild(i)->getOctant() == octant)
-					{
-						node = node->getChild(i);
-						octant = node->getOctant(pos.mdV);
-						keep_going = TRUE;
-					}
-				}
+				node = node->getChild(next_node);
+				octant = node->getOctant(pos);
+				next_node = node->mChildMap[octant];
 			}
 		}
 		else if (!node->contains(rad) && node->getParent())
@@ -276,7 +286,7 @@ class LLOctreeNode : public LLTreeNode<T>
 	{
 		if (data == NULL)
 		{
-			//OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl;
+			OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl;
 			return false;
 		}
 		LLOctreeNode<T>* parent = getOctParent();
@@ -284,10 +294,8 @@ class LLOctreeNode : public LLTreeNode<T>
 		//is it here?
 		if (isInside(data->getPositionGroup()))
 		{
-			if (getElementCount() < LL_OCTREE_MAX_CAPACITY &&
-				(contains(data->getBinRadius()) ||
-				(data->getBinRadius() > getSize().mdV[0] &&
-				parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY))) 
+			if ((getElementCount() < LL_OCTREE_MAX_CAPACITY && contains(data->getBinRadius()) ||
+				(data->getBinRadius() > getSize()[0] &&	parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY))) 
 			{ //it belongs here
 #if LL_OCTREE_PARANOIA_CHECK
 				//if this is a redundant insertion, error out (should never happen)
@@ -317,16 +325,21 @@ class LLOctreeNode : public LLTreeNode<T>
 				}
 				
 				//it's here, but no kids are in the right place, make a new kid
-				LLVector3d center(getCenter());
-				LLVector3d size(getSize()*0.5);
+				LLVector4a center = getCenter();
+				LLVector4a size = getSize();
+				size.mul(0.5f);
 		        		
 				//push center in direction of data
 				LLOctreeNode<T>::pushCenter(center, size, data);
 
 				// handle case where floating point number gets too small
-				if( llabs(center.mdV[0] - getCenter().mdV[0]) < F_APPROXIMATELY_ZERO &&
-					llabs(center.mdV[1] - getCenter().mdV[1]) < F_APPROXIMATELY_ZERO &&
-					llabs(center.mdV[2] - getCenter().mdV[2]) < F_APPROXIMATELY_ZERO)
+				LLVector4a val;
+				val.setSub(center, getCenter());
+				val.setAbs(val);
+								
+				S32 lt = val.lessThan(LLVector4a::getEpsilon()).getGatheredBits() & 0x7;
+
+				if( lt == 0x7 )
 				{
 					mData.insert(data);
 					BaseType::insert(data);
@@ -344,7 +357,7 @@ class LLOctreeNode : public LLTreeNode<T>
 				//make sure no existing node matches this position
 				for (U32 i = 0; i < getChildCount(); i++)
 				{
-					if (mChild[i]->getCenter() == center)
+					if (mChild[i]->getCenter().equals3(center))
 					{
 						OCT_ERRS << "Octree detected duplicate child center and gave up." << llendl;
 						return false;
@@ -362,7 +375,7 @@ class LLOctreeNode : public LLTreeNode<T>
 		else 
 		{
 			//it's not in here, give it to the root
-			//OCT_ERRS << "Octree insertion failed, starting over from root!" << llendl;
+			OCT_ERRS << "Octree insertion failed, starting over from root!" << llendl;
 
 			oct_node* node = this;
 
@@ -436,6 +449,9 @@ class LLOctreeNode : public LLTreeNode<T>
 	void clearChildren()
 	{
 		mChild.clear();
+
+		U32* foo = (U32*) mChildMap;
+		foo[0] = foo[1] = 0xFFFFFFFF;
 	}
 
 	void validate()
@@ -469,13 +485,19 @@ class LLOctreeNode : public LLTreeNode<T>
 	void addChild(oct_node* child, BOOL silent = FALSE) 
 	{
 #if LL_OCTREE_PARANOIA_CHECK
+
+		if (child->getSize().equals3(getSize()))
+		{
+			OCT_ERRS << "Child size is same as parent size!" << llendl;
+		}
+
 		for (U32 i = 0; i < getChildCount(); i++)
 		{
-			if(mChild[i]->getSize() != child->getSize()) 
+			if(!mChild[i]->getSize().equals3(child->getSize())) 
 			{
 				OCT_ERRS <<"Invalid octree child size." << llendl;
 			}
-			if (mChild[i]->getCenter() == child->getCenter())
+			if (mChild[i]->getCenter().equals3(child->getCenter()))
 			{
 				OCT_ERRS <<"Duplicate octree child position." << llendl;
 			}
@@ -487,6 +509,8 @@ class LLOctreeNode : public LLTreeNode<T>
 		}
 #endif
 
+		mChildMap[child->getOctant()] = (U8) mChild.size();
+
 		mChild.push_back(child);
 		child->setParent(this);
 
@@ -500,7 +524,7 @@ class LLOctreeNode : public LLTreeNode<T>
 		}
 	}
 
-	void removeChild(U8 index, BOOL destroy = FALSE)
+	void removeChild(S32 index, BOOL destroy = FALSE)
 	{
 		for (U32 i = 0; i < this->getListenerCount(); i++)
 		{
@@ -508,6 +532,8 @@ class LLOctreeNode : public LLTreeNode<T>
 			listener->handleChildRemoval(this, getChild(index));
 		}
 
+		
+
 		if (destroy)
 		{
 			mChild[index]->destroy();
@@ -515,6 +541,15 @@ class LLOctreeNode : public LLTreeNode<T>
 		}
 		mChild.erase(mChild.begin() + index);
 
+		//rebuild child map
+		U32* foo = (U32*) mChildMap;
+		foo[0] = foo[1] = 0xFFFFFFFF;
+
+		for (U32 i = 0; i < mChild.size(); ++i)
+		{
+			mChildMap[mChild[i]->getOctant()] = i;
+		}
+
 		checkAlive();
 	}
 
@@ -541,19 +576,32 @@ class LLOctreeNode : public LLTreeNode<T>
 			}
 		}
 
-		//OCT_ERRS << "Octree failed to delete requested child." << llendl;
+		OCT_ERRS << "Octree failed to delete requested child." << llendl;
 	}
 
 protected:	
-	child_list mChild;
-	element_list mData;
+	typedef enum
+	{
+		CENTER = 0,
+		SIZE = 1,
+		MAX = 2,
+		MIN = 3
+	} eDName;
+
+	LLVector4a mCenter;
+	LLVector4a mSize;
+	LLVector4a mMax;
+	LLVector4a mMin;
+	
 	oct_node* mParent;
-	LLVector3d mCenter;
-	LLVector3d mSize;
-	LLVector3d mMax;
-	LLVector3d mMin;
 	U8 mOctant;
-};
+
+	child_list mChild;
+	U8 mChildMap[8];
+
+	element_list mData;
+		
+}; 
 
 //just like a regular node, except it might expand on insert and compress on balance
 template <class T>
@@ -563,9 +611,9 @@ class LLOctreeRoot : public LLOctreeNode<T>
 	typedef LLOctreeNode<T>	BaseType;
 	typedef LLOctreeNode<T>		oct_node;
 
-	LLOctreeRoot(	LLVector3d center, 
-					LLVector3d size, 
-					BaseType* parent)
+	LLOctreeRoot(const LLVector4a& center, 
+				 const LLVector4a& size, 
+				 BaseType* parent)
 	:	BaseType(center, size, parent)
 	{
 	}
@@ -596,6 +644,8 @@ class LLOctreeRoot : public LLOctreeNode<T>
 			//destroy child
 			child->clearChildren();
 			delete child;
+
+			return false;
 		}
 		
 		return true;
@@ -606,28 +656,33 @@ class LLOctreeRoot : public LLOctreeNode<T>
 	{
 		if (data == NULL) 
 		{
-			//OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << llendl;
+			OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << llendl;
 			return false;
 		}
 		
 		if (data->getBinRadius() > 4096.0)
 		{
-			//OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << llendl;
+			OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << llendl;
 			return false;
 		}
 		
-		const F64 MAX_MAG = 1024.0*1024.0;
+		LLVector4a MAX_MAG;
+		MAX_MAG.splat(1024.f*1024.f);
 
-		const LLVector3d& v = data->getPositionGroup();
-		if (!(fabs(v.mdV[0]-this->mCenter.mdV[0]) < MAX_MAG &&
-		      fabs(v.mdV[1]-this->mCenter.mdV[1]) < MAX_MAG &&
-		      fabs(v.mdV[2]-this->mCenter.mdV[2]) < MAX_MAG))
+		const LLVector4a& v = data->getPositionGroup();
+
+		LLVector4a val;
+		val.setSub(v, BaseType::mCenter);
+		val.setAbs(val);
+		S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7;
+
+		if (lt != 0x7)
 		{
-			//OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << llendl;
+			OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << llendl;
 			return false;
 		}
 
-		if (this->getSize().mdV[0] > data->getBinRadius() && isInside(data->getPositionGroup()))
+		if (this->getSize()[0] > data->getBinRadius() && isInside(data->getPositionGroup()))
 		{
 			//we got it, just act like a branch
 			oct_node* node = getNodeAt(data);
@@ -643,31 +698,34 @@ class LLOctreeRoot : public LLOctreeNode<T>
 		else if (this->getChildCount() == 0)
 		{
 			//first object being added, just wrap it up
-			while (!(this->getSize().mdV[0] > data->getBinRadius() && isInside(data->getPositionGroup())))
+			while (!(this->getSize()[0] > data->getBinRadius() && isInside(data->getPositionGroup())))
 			{
-				LLVector3d center, size;
+				LLVector4a center, size;
 				center = this->getCenter();
 				size = this->getSize();
 				LLOctreeNode<T>::pushCenter(center, size, data);
 				this->setCenter(center);
-				this->setSize(size*2);
+				size.mul(2.f);
+				this->setSize(size);
 				this->updateMinMax();
 			}
 			LLOctreeNode<T>::insert(data);
 		}
 		else
 		{
-			while (!(this->getSize().mdV[0] > data->getBinRadius() && isInside(data->getPositionGroup())))
+			while (!(this->getSize()[0] > data->getBinRadius() && isInside(data->getPositionGroup())))
 			{
 				//the data is outside the root node, we need to grow
-				LLVector3d center(this->getCenter());
-				LLVector3d size(this->getSize());
+				LLVector4a center(this->getCenter());
+				LLVector4a size(this->getSize());
 
 				//expand this node
-				LLVector3d newcenter(center);
+				LLVector4a newcenter(center);
 				LLOctreeNode<T>::pushCenter(newcenter, size, data);
 				this->setCenter(newcenter);
-				this->setSize(size*2);
+				LLVector4a size2 = size;
+				size2.mul(2.f);
+				this->setSize(size2);
 				this->updateMinMax();
 
 				//copy our children to a new branch
@@ -704,4 +762,15 @@ void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
 		traverse(node->getChild(i));
 	}
 }
+
+template <class T>
+void LLOctreeTravelerDepthFirst<T>::traverse(const LLOctreeNode<T>* node)
+{
+	for (U32 i = 0; i < node->getChildCount(); i++)
+	{
+		traverse(node->getChild(i));
+	}
+	node->accept(this);
+}
+
 #endif
diff --git a/indra/llmath/llplane.h b/indra/llmath/llplane.h
index 443f3f46b9b2cea44a2c5568bd1ce66ca024315d..a611894721c9207d829fdea615feb0a13c6616ec 100644
--- a/indra/llmath/llplane.h
+++ b/indra/llmath/llplane.h
@@ -36,19 +36,23 @@
 // The plane normal = [A, B, C]
 // The closest approach = D / sqrt(A*A + B*B + C*C)
 
-class LLPlane : public LLVector4
+class LLPlane
 {
 public:
+	
+	// Constructors
 	LLPlane() {}; // no default constructor
 	LLPlane(const LLVector3 &p0, F32 d) { setVec(p0, d); }
 	LLPlane(const LLVector3 &p0, const LLVector3 &n) { setVec(p0, n); }
-	void setVec(const LLVector3 &p0, F32 d) { LLVector4::setVec(p0[0], p0[1], p0[2], d); }
-	void setVec(const LLVector3 &p0, const LLVector3 &n)
+	inline void setVec(const LLVector3 &p0, F32 d) { mV.set(p0[0], p0[1], p0[2], d); }
+	
+	// Set
+	inline void setVec(const LLVector3 &p0, const LLVector3 &n)
 	{
 		F32 d = -(p0 * n);
 		setVec(n, d);
 	}
-	void setVec(const LLVector3 &p0, const LLVector3 &p1, const LLVector3 &p2)
+	inline void setVec(const LLVector3 &p0, const LLVector3 &p1, const LLVector3 &p2)
 	{
 		LLVector3 u, v, w;
 		u = p1 - p0;
@@ -58,8 +62,38 @@ class LLPlane : public LLVector4
 		F32 d = -(w * p0);
 		setVec(w, d);
 	}
-	LLPlane& operator=(const LLVector4& v2) {  LLVector4::setVec(v2[0],v2[1],v2[2],v2[3]); return *this;}
+	
+	inline LLPlane& operator=(const LLVector4& v2) {  mV.set(v2[0],v2[1],v2[2],v2[3]); return *this;}
+	
+	inline LLPlane& operator=(const LLVector4a& v2) {  mV.set(v2[0],v2[1],v2[2],v2[3]); return *this;}	
+	
+	inline void set(const LLPlane& p2) { mV = p2.mV; }
+	
+	// 
 	F32 dist(const LLVector3 &v2) const { return mV[0]*v2[0] + mV[1]*v2[1] + mV[2]*v2[2] + mV[3]; }
+	
+	inline LLSimdScalar dot3(const LLVector4a& b) const { return mV.dot3(b); }
+	
+	// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates
+	// the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead	
+	inline F32 operator[](const S32 idx) const { return mV[idx]; }
+	
+	// preferable when index is known at compile time
+	template <int N> LL_FORCE_INLINE void getAt(LLSimdScalar& v) const { v = mV.getScalarAt<N>(); } 
+	
+	// reset the vector to 0, 0, 0, 1
+	inline void clear() { mV.set(0, 0, 0, 1); }
+	
+	inline void getVector3(LLVector3& vec) const { vec.set(mV[0], mV[1], mV[2]); }
+	
+	// Retrieve the mask indicating which of the x, y, or z axis are greater or equal to zero.
+	inline U8 calcPlaneMask() 
+	{ 
+		return mV.greaterEqual(LLVector4a::getZero()).getGatheredBits() & LLVector4Logical::MASK_XYZ;
+	}
+		
+private:
+	LLVector4a mV;
 };
 
 
diff --git a/indra/llmath/llquantize.h b/indra/llmath/llquantize.h
index 7f56ff3448d93b61d673a103576f4bb942509e29..1595dbecf8d7369d7a1f6b1d469db72c230b87c1 100644
--- a/indra/llmath/llquantize.h
+++ b/indra/llmath/llquantize.h
@@ -29,10 +29,16 @@
 #define LL_LLQUANTIZE_H
 
 const U16 U16MAX = 65535;
+LL_ALIGN_16( const F32 F_U16MAX_4A[4] ) = { 65535.f, 65535.f, 65535.f, 65535.f };
+
 const F32 OOU16MAX = 1.f/(F32)(U16MAX);
+LL_ALIGN_16( const F32 F_OOU16MAX_4A[4] ) = { OOU16MAX, OOU16MAX, OOU16MAX, OOU16MAX };
 
 const U8 U8MAX = 255;
+LL_ALIGN_16( const F32 F_U8MAX_4A[4] ) = { 255.f, 255.f, 255.f, 255.f };
+
 const F32 OOU8MAX = 1.f/(F32)(U8MAX);
+LL_ALIGN_16( const F32 F_OOU8MAX_4A[4] ) = { OOU8MAX, OOU8MAX, OOU8MAX, OOU8MAX };
 
 const U8 FIRSTVALIDCHAR = 54;
 const U8 MAXSTRINGVAL = U8MAX - FIRSTVALIDCHAR; //we don't allow newline or null 
diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp
index a51f11072cb1a33de7f7b30ffc62a590a6786102..7381d5eb99e7552b176b7c13d355191b2f60c775 100644
--- a/indra/llmath/llquaternion.cpp
+++ b/indra/llmath/llquaternion.cpp
@@ -26,9 +26,10 @@
 
 #include "linden_common.h"
 
+#include "llmath.h"	// for F_PI
+
 #include "llquaternion.h"
 
-#include "llmath.h"	// for F_PI
 //#include "vmath.h"
 #include "v3math.h"
 #include "v3dmath.h"
diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h
index 26da14ae20526ff5215a2f041d44f2b3f2d00d20..ca0dfe206bd5ff31007583770cf4a42f33470813 100644
--- a/indra/llmath/llquaternion.h
+++ b/indra/llmath/llquaternion.h
@@ -27,7 +27,11 @@
 #ifndef LLQUATERNION_H
 #define LLQUATERNION_H
 
-#include "llmath.h"
+#include <iostream>
+
+#ifndef LLMATH_H //enforce specific include order to avoid tangling inline dependencies
+#error "Please include llmath.h first."
+#endif
 
 class LLVector4;
 class LLVector3;
diff --git a/indra/llmath/llquaternion2.h b/indra/llmath/llquaternion2.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd9c0cf3abad680578594505b12a67e75998ade0
--- /dev/null
+++ b/indra/llmath/llquaternion2.h
@@ -0,0 +1,105 @@
+/** 
+ * @file llquaternion2.h
+ * @brief LLQuaternion2 class header file - SIMD-enabled quaternion class
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef	LL_QUATERNION2_H
+#define	LL_QUATERNION2_H
+
+/////////////////////////////
+// LLQuaternion2
+/////////////////////////////
+// This class stores a quaternion x*i + y*j + z*k + w in <x, y, z, w> order
+// (i.e., w in high order element of vector)
+/////////////////////////////
+/////////////////////////////
+// These classes are intentionally minimal right now. If you need additional
+// functionality, please contact someone with SSE experience (e.g., Falcon or
+// Huseby).
+/////////////////////////////
+#include "llquaternion.h"
+
+class LLQuaternion2
+{
+public:
+
+	//////////////////////////
+	// Ctors
+	//////////////////////////
+	
+	// Ctor
+	LLQuaternion2() {}
+
+	// Ctor from LLQuaternion
+	explicit LLQuaternion2( const class LLQuaternion& quat );
+
+	//////////////////////////
+	// Get/Set
+	//////////////////////////
+
+	// Load from an LLQuaternion
+	inline void operator=( const LLQuaternion& quat )
+	{
+		mQ.loadua( quat.mQ );
+	}
+
+	// Return the internal LLVector4a representation of the quaternion
+	inline const LLVector4a& getVector4a() const;
+	inline LLVector4a& getVector4aRw();
+
+	/////////////////////////
+	// Quaternion modification
+	/////////////////////////
+	
+	// Set this quaternion to the conjugate of src
+	inline void setConjugate(const LLQuaternion2& src);
+
+	// Renormalizes the quaternion. Assumes it has nonzero length.
+	inline void normalize();
+
+	// Quantize this quaternion to 8 bit precision
+	inline void quantize8();
+
+	// Quantize this quaternion to 16 bit precision
+	inline void quantize16();
+
+	/////////////////////////
+	// Quaternion inspection
+	/////////////////////////
+
+	// Return true if this quaternion is equal to 'rhs'. 
+	// Note! Quaternions exhibit "double-cover", so any rotation has two equally valid
+	// quaternion representations and they will NOT compare equal.
+	inline bool equals(const LLQuaternion2& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+	// Return true if all components are finite and the quaternion is normalized
+	inline bool isOkRotation() const;
+
+protected:
+
+	LLVector4a mQ;
+
+};
+
+#endif
diff --git a/indra/llmath/llquaternion2.inl b/indra/llmath/llquaternion2.inl
new file mode 100644
index 0000000000000000000000000000000000000000..2a6987552d2949dcf392c1c00603ff70a97a86ea
--- /dev/null
+++ b/indra/llmath/llquaternion2.inl
@@ -0,0 +1,102 @@
+/** 
+ * @file llquaternion2.inl
+ * @brief LLQuaternion2 inline definitions
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llquaternion2.h"
+
+static const LLQuad LL_V4A_PLUS_ONE = {1.f, 1.f, 1.f, 1.f};
+static const LLQuad LL_V4A_MINUS_ONE = {-1.f, -1.f, -1.f, -1.f};
+
+// Ctor from LLQuaternion
+inline LLQuaternion2::LLQuaternion2( const LLQuaternion& quat )
+{
+	mQ.set(quat.mQ[VX], quat.mQ[VY], quat.mQ[VZ], quat.mQ[VW]);
+}
+
+//////////////////////////
+// Get/Set
+//////////////////////////
+
+// Return the internal LLVector4a representation of the quaternion
+inline const LLVector4a& LLQuaternion2::getVector4a() const
+{
+	return mQ;
+}
+
+inline LLVector4a& LLQuaternion2::getVector4aRw()
+{
+	return mQ;
+}
+
+/////////////////////////
+// Quaternion modification
+/////////////////////////
+
+// Set this quaternion to the conjugate of src
+inline void LLQuaternion2::setConjugate(const LLQuaternion2& src)
+{
+	static LL_ALIGN_16( const U32 F_QUAT_INV_MASK_4A[4] ) = { 0x80000000, 0x80000000, 0x80000000, 0x00000000 };
+	mQ = _mm_xor_ps(src.mQ, *reinterpret_cast<const LLQuad*>(&F_QUAT_INV_MASK_4A));	
+}
+
+// Renormalizes the quaternion. Assumes it has nonzero length.
+inline void LLQuaternion2::normalize()
+{
+	mQ.normalize4();
+}
+
+// Quantize this quaternion to 8 bit precision
+inline void LLQuaternion2::quantize8()
+{
+	mQ.quantize8( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE );
+	normalize();
+}
+
+// Quantize this quaternion to 16 bit precision
+inline void LLQuaternion2::quantize16()
+{
+	mQ.quantize16( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE );
+	normalize();
+}
+
+
+/////////////////////////
+// Quaternion inspection
+/////////////////////////
+
+// Return true if this quaternion is equal to 'rhs'. 
+// Note! Quaternions exhibit "double-cover", so any rotation has two equally valid
+// quaternion representations and they will NOT compare equal.
+inline bool LLQuaternion2::equals(const LLQuaternion2 &rhs, F32 tolerance/* = F_APPROXIMATELY_ZERO*/) const
+{
+	return mQ.equals4(rhs.mQ, tolerance);
+}
+
+// Return true if all components are finite and the quaternion is normalized
+inline bool LLQuaternion2::isOkRotation() const
+{
+	return mQ.isFinite4() && mQ.isNormalized4();
+}
+
diff --git a/indra/llmath/llsimdmath.h b/indra/llmath/llsimdmath.h
new file mode 100644
index 0000000000000000000000000000000000000000..c7cdf7b32cb5d8d729fb42f7ef383424e9923268
--- /dev/null
+++ b/indra/llmath/llsimdmath.h
@@ -0,0 +1,93 @@
+/** 
+ * @file llsimdmath.h
+ * @brief Common header for SIMD-based math library (llvector4a, llmatrix3a, etc.)
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef	LL_SIMD_MATH_H
+#define	LL_SIMD_MATH_H
+
+#ifndef LLMATH_H
+#error "Please include llmath.h before this file."
+#endif
+
+#if ( ( LL_DARWIN || LL_LINUX ) && !(__SSE2__) ) || ( LL_WINDOWS && ( _M_IX86_FP < 2 ) )
+#error SSE2 not enabled. LLVector4a and related class will not compile.
+#endif
+
+#if !LL_WINDOWS
+#include <stdint.h>
+#endif
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address) 
+{ 
+	return reinterpret_cast<T*>(
+		(reinterpret_cast<uintptr_t>(address) + 0xF) & ~0xF);
+}
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) 
+{ 
+	return reinterpret_cast<T*>(
+		(reinterpret_cast<uintptr_t>(address) + 0x3F) & ~0x3F);
+}
+
+#if LL_LINUX || LL_DARWIN
+
+#define			LL_ALIGN_PREFIX(x)
+#define			LL_ALIGN_POSTFIX(x)		__attribute__((aligned(x)))
+
+#elif LL_WINDOWS
+
+#define			LL_ALIGN_PREFIX(x)		__declspec(align(x))
+#define			LL_ALIGN_POSTFIX(x)
+
+#else
+#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined"
+#endif
+
+#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16)
+
+
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#include "llsimdtypes.h"
+#include "llsimdtypes.inl"
+
+class LLMatrix3a;
+class LLRotation;
+class LLMatrix3;
+
+#include "llquaternion.h"
+
+#include "llvector4logical.h"
+#include "llvector4a.h"
+#include "llmatrix3a.h"
+#include "llquaternion2.h"
+#include "llvector4a.inl"
+#include "llmatrix3a.inl"
+#include "llquaternion2.inl"
+
+
+#endif //LL_SIMD_MATH_H
diff --git a/indra/llmath/llsimdtypes.h b/indra/llmath/llsimdtypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd991d0e71d57bf7a9e97007b60fc337dd721ca8
--- /dev/null
+++ b/indra/llmath/llsimdtypes.h
@@ -0,0 +1,124 @@
+/** 
+ * @file llsimdtypes.h
+ * @brief Declaration of basic SIMD math related types
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SIMD_TYPES_H
+#define LL_SIMD_TYPES_H
+
+#ifndef LL_SIMD_MATH_H
+#error "Please include llmath.h before this file."
+#endif
+
+typedef __m128	LLQuad;
+
+
+#if LL_WINDOWS
+#pragma warning(push)
+#pragma warning( disable : 4800 3 ) // Disable warning about casting int to bool for this class.
+#if defined(_MSC_VER) && (_MSC_VER < 1500)
+// VC++ 2005 is missing these intrinsics
+// __forceinline is MSVC specific and attempts to override compiler inlining judgment. This is so
+// even in debug builds this call is a NOP.
+__forceinline const __m128 _mm_castsi128_ps( const __m128i a ) { return reinterpret_cast<const __m128&>(a); }
+__forceinline const __m128i _mm_castps_si128( const __m128 a ) { return reinterpret_cast<const __m128i&>(a); }
+#endif // _MSC_VER
+
+#endif // LL_WINDOWS
+
+class LLBool32
+{
+public:
+	inline LLBool32() {}
+	inline LLBool32(int rhs) : m_bool(rhs) {}
+	inline LLBool32(unsigned int rhs) : m_bool(rhs) {}
+	inline LLBool32(bool rhs) { m_bool = static_cast<const int>(rhs); }
+	inline LLBool32& operator= (bool rhs) { m_bool = (int)rhs; return *this; }
+	inline bool operator== (bool rhs) const { return static_cast<const bool&>(m_bool) == rhs; }
+	inline bool operator!= (bool rhs) const { return !operator==(rhs); }
+	inline operator bool() const { return static_cast<const bool&>(m_bool); }
+
+private:
+	int m_bool;
+};
+
+#if LL_WINDOWS
+#pragma warning(pop)
+#endif
+
+class LLSimdScalar
+{
+public:
+	inline LLSimdScalar() {}
+	inline LLSimdScalar(LLQuad q) 
+	{ 
+		mQ = q; 
+	}
+
+	inline LLSimdScalar(F32 f) 
+	{ 
+		mQ = _mm_set_ss(f); 
+	}
+
+	static inline const LLSimdScalar& getZero()
+	{
+		extern const LLQuad F_ZERO_4A;
+		return reinterpret_cast<const LLSimdScalar&>(F_ZERO_4A);
+	}
+
+	inline F32 getF32() const;
+
+	inline LLBool32 isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance = F_APPROXIMATELY_ZERO) const;
+
+	inline LLSimdScalar getAbs() const;
+
+	inline void setMax( const LLSimdScalar& a, const LLSimdScalar& b );
+	
+	inline void setMin( const LLSimdScalar& a, const LLSimdScalar& b );
+
+	inline LLSimdScalar& operator=(F32 rhs);
+
+	inline LLSimdScalar& operator+=(const LLSimdScalar& rhs);
+
+	inline LLSimdScalar& operator-=(const LLSimdScalar& rhs);
+
+	inline LLSimdScalar& operator*=(const LLSimdScalar& rhs);
+
+	inline LLSimdScalar& operator/=(const LLSimdScalar& rhs);
+
+	inline operator LLQuad() const
+	{ 
+		return mQ; 
+	}
+	
+	inline const LLQuad& getQuad() const 
+	{ 
+		return mQ; 
+	}
+
+private:
+	LLQuad mQ;
+};
+
+#endif //LL_SIMD_TYPES_H
diff --git a/indra/llmath/llsimdtypes.inl b/indra/llmath/llsimdtypes.inl
new file mode 100644
index 0000000000000000000000000000000000000000..712239e4258b6bb4ed2ecb41ac45db5e2908a7ab
--- /dev/null
+++ b/indra/llmath/llsimdtypes.inl
@@ -0,0 +1,157 @@
+/** 
+ * @file llsimdtypes.inl
+ * @brief Inlined definitions of basic SIMD math related types
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+
+
+
+//////////////////
+// LLSimdScalar
+//////////////////
+
+inline LLSimdScalar operator+(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	LLSimdScalar t(a);
+	t += b;
+	return t;
+}
+
+inline LLSimdScalar operator-(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	LLSimdScalar t(a);
+	t -= b;
+	return t;
+}
+
+inline LLSimdScalar operator*(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	LLSimdScalar t(a);
+	t *= b;
+	return t;
+}
+
+inline LLSimdScalar operator/(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	LLSimdScalar t(a);
+	t /= b;
+	return t;
+}
+
+inline LLSimdScalar operator-(const LLSimdScalar& a)
+{
+	static LL_ALIGN_16(const U32 signMask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000 };
+	return _mm_xor_ps(*reinterpret_cast<const LLQuad*>(signMask), a);
+}
+
+inline LLBool32 operator==(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	return _mm_comieq_ss(a, b);
+}
+
+inline LLBool32 operator!=(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	return _mm_comineq_ss(a, b);
+}
+
+inline LLBool32 operator<(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	return _mm_comilt_ss(a, b);
+}
+
+inline LLBool32 operator<=(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	return _mm_comile_ss(a, b);
+}
+
+inline LLBool32 operator>(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	return _mm_comigt_ss(a, b);
+}
+
+inline LLBool32 operator>=(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+	return _mm_comige_ss(a, b);
+}
+
+inline LLBool32 LLSimdScalar::isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance /* = F_APPROXIMATELY_ZERO */) const
+{
+	const LLSimdScalar tol( tolerance );
+	const LLSimdScalar diff = _mm_sub_ss( mQ, rhs.mQ );
+	const LLSimdScalar absDiff = diff.getAbs();
+	return absDiff <= tol;
+}
+
+inline void LLSimdScalar::setMax( const LLSimdScalar& a, const LLSimdScalar& b )
+{
+	mQ = _mm_max_ss( a, b );
+}
+
+inline void LLSimdScalar::setMin( const LLSimdScalar& a, const LLSimdScalar& b )
+{
+	mQ = _mm_min_ss( a, b );
+}
+
+inline LLSimdScalar& LLSimdScalar::operator=(F32 rhs) 
+{ 
+	mQ = _mm_set_ss(rhs); 
+	return *this; 
+}
+
+inline LLSimdScalar& LLSimdScalar::operator+=(const LLSimdScalar& rhs) 
+{
+	mQ = _mm_add_ss( mQ, rhs );
+	return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator-=(const LLSimdScalar& rhs)
+{
+	mQ = _mm_sub_ss( mQ, rhs );
+	return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator*=(const LLSimdScalar& rhs)
+{
+	mQ = _mm_mul_ss( mQ, rhs );
+	return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator/=(const LLSimdScalar& rhs)
+{
+	mQ = _mm_div_ss( mQ, rhs );
+	return *this;
+}
+
+inline LLSimdScalar LLSimdScalar::getAbs() const
+{
+	static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
+	return _mm_and_ps( mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A));
+}
+
+inline F32 LLSimdScalar::getF32() const
+{ 
+	F32 ret; 
+	_mm_store_ss(&ret, mQ); 
+	return ret; 
+}
diff --git a/indra/llmath/lltreenode.h b/indra/llmath/lltreenode.h
index a462d1659ebfe836bffb04d75592a01ce43e8f84..c66bc26176130730e5da24c4cc0d0eb1e04ffe4e 100644
--- a/indra/llmath/lltreenode.h
+++ b/indra/llmath/lltreenode.h
@@ -28,6 +28,9 @@
 
 #include "stdtypes.h"
 #include "xform.h"
+#include "llpointer.h"
+#include "llrefcount.h"
+
 #include <vector>
 
 template <class T> class LLTreeNode;
diff --git a/indra/llmath/llvector4a.cpp b/indra/llmath/llvector4a.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b66b7a70764f414480d4ab5c5c6cf7ce2ba848ca
--- /dev/null
+++ b/indra/llmath/llvector4a.cpp
@@ -0,0 +1,222 @@
+/** 
+ * @file llvector4a.cpp
+ * @brief SIMD vector implementation
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llmath.h"
+#include "llquantize.h"
+
+extern const LLQuad F_ZERO_4A		= { 0, 0, 0, 0 };
+extern const LLQuad F_APPROXIMATELY_ZERO_4A = { 
+	F_APPROXIMATELY_ZERO,
+	F_APPROXIMATELY_ZERO,
+	F_APPROXIMATELY_ZERO,
+	F_APPROXIMATELY_ZERO
+};
+
+extern const LLVector4a LL_V4A_ZERO = reinterpret_cast<const LLVector4a&> ( F_ZERO_4A );
+extern const LLVector4a LL_V4A_EPSILON = reinterpret_cast<const LLVector4a&> ( F_APPROXIMATELY_ZERO_4A );
+
+/*static */void LLVector4a::memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes)
+{
+	assert(src != NULL);
+	assert(dst != NULL);
+	assert(bytes > 0);
+	assert((bytes % sizeof(F32))== 0); 
+	
+	F32* end = dst + (bytes / sizeof(F32) );
+
+	if (bytes > 64)
+	{
+		F32* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst);
+		
+		//at least 64 (16*4) bytes before the end of the destination, switch to 16 byte copies
+		F32* end_64 = end-16;
+		
+		_mm_prefetch((char*)begin_64, _MM_HINT_NTA);
+		_mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA);
+		_mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA);
+		_mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA);
+		
+		while (dst < begin_64)
+		{
+			copy4a(dst, src);
+			dst += 4;
+			src += 4;
+		}
+		
+		while (dst < end_64)
+		{
+			_mm_prefetch((char*)src + 512, _MM_HINT_NTA);
+			_mm_prefetch((char*)dst + 512, _MM_HINT_NTA);
+			copy4a(dst, src);
+			copy4a(dst+4, src+4);
+			copy4a(dst+8, src+8);
+			copy4a(dst+12, src+12);
+			
+			dst += 16;
+			src += 16;
+		}
+	}
+
+	while (dst < end)
+	{
+		copy4a(dst, src);
+		dst += 4;
+		src += 4;
+	}
+}
+
+void LLVector4a::setRotated( const LLRotation& rot, const LLVector4a& vec )
+{
+	const LLVector4a col0 = rot.getColumn(0);
+	const LLVector4a col1 = rot.getColumn(1);
+	const LLVector4a col2 = rot.getColumn(2);
+
+	LLVector4a result = _mm_load_ss( vec.getF32ptr() );
+	result.splat<0>( result );
+	result.mul( col0 );
+
+	{
+		LLVector4a yyyy = _mm_load_ss( vec.getF32ptr() +  1 );
+		yyyy.splat<0>( yyyy );
+		yyyy.mul( col1 ); 
+		result.add( yyyy );
+	}
+
+	{
+		LLVector4a zzzz = _mm_load_ss( vec.getF32ptr() +  2 );
+		zzzz.splat<0>( zzzz );
+		zzzz.mul( col2 );
+		result.add( zzzz );
+	}
+
+	*this = result;
+}
+
+void LLVector4a::setRotated( const LLQuaternion2& quat, const LLVector4a& vec )
+{
+	const LLVector4a& quatVec = quat.getVector4a();
+	LLVector4a temp; temp.setCross3(quatVec, vec);
+	temp.add( temp );
+	
+	const LLVector4a realPart( quatVec.getScalarAt<3>() );
+	LLVector4a tempTimesReal; tempTimesReal.setMul( temp, realPart );
+
+	mQ = vec;
+	add( tempTimesReal );
+	
+	LLVector4a imagCrossTemp; imagCrossTemp.setCross3( quatVec, temp );
+	add(imagCrossTemp);
+}
+
+void LLVector4a::quantize8( const LLVector4a& low, const LLVector4a& high )
+{
+	LLVector4a val(mQ);
+	LLVector4a delta; delta.setSub( high, low );
+
+	{
+		val.clamp(low, high);
+		val.sub(low);
+
+		// 8-bit quantization means we can do with just 12 bits of reciprocal accuracy
+		const LLVector4a oneOverDelta = _mm_rcp_ps(delta.mQ);
+// 		{
+// 			static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f };
+// 			LLVector4a two; two.load4a( F_TWO_4A );
+// 
+// 			// Here we use _mm_rcp_ps plus one round of newton-raphson
+// 			// We wish to find 'x' such that x = 1/delta
+// 			// As a first approximation, we take x0 = _mm_rcp_ps(delta)
+// 			// Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 )
+// 			// See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf
+// 			const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ);
+// 			oneOverDelta.setMul( delta, recipApprox );
+// 			oneOverDelta.setSub( two, oneOverDelta );
+// 			oneOverDelta.mul( recipApprox );
+// 		}
+
+		val.mul(oneOverDelta);
+		val.mul(*reinterpret_cast<const LLVector4a*>(F_U8MAX_4A));
+	}
+
+	val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ ));
+
+	{
+		val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A));
+		val.mul(delta);
+		val.add(low);
+	}
+
+	{
+		LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A));
+		LLVector4a absVal; absVal.setAbs( val );
+		setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val );
+	}	
+}
+
+void LLVector4a::quantize16( const LLVector4a& low, const LLVector4a& high )
+{
+	LLVector4a val(mQ);
+	LLVector4a delta; delta.setSub( high, low );
+
+	{
+		val.clamp(low, high);
+		val.sub(low);
+
+		// 16-bit quantization means we need a round of Newton-Raphson
+		LLVector4a oneOverDelta;
+		{
+			static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f };
+			LLVector4a two; two.load4a( F_TWO_4A );
+
+			// Here we use _mm_rcp_ps plus one round of newton-raphson
+			// We wish to find 'x' such that x = 1/delta
+			// As a first approximation, we take x0 = _mm_rcp_ps(delta)
+			// Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 )
+			// See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf
+			const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ);
+			oneOverDelta.setMul( delta, recipApprox );
+			oneOverDelta.setSub( two, oneOverDelta );
+			oneOverDelta.mul( recipApprox );
+		}
+
+		val.mul(oneOverDelta);
+		val.mul(*reinterpret_cast<const LLVector4a*>(F_U16MAX_4A));
+	}
+
+	val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ ));
+
+	{
+		val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A));
+		val.mul(delta);
+		val.add(low);
+	}
+
+	{
+		LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A));
+		LLVector4a absVal; absVal.setAbs( val );
+		setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val );
+	}	
+}
diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h
new file mode 100644
index 0000000000000000000000000000000000000000..596082509df104fd09651ac3366975885f6f3d2e
--- /dev/null
+++ b/indra/llmath/llvector4a.h
@@ -0,0 +1,324 @@
+/** 
+ * @file llvector4a.h
+ * @brief LLVector4a class header file - memory aligned and vectorized 4 component vector
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef	LL_LLVECTOR4A_H
+#define	LL_LLVECTOR4A_H
+
+
+class LLRotation;
+
+#include <assert.h>
+#include "llpreprocessor.h"
+
+///////////////////////////////////
+// FIRST TIME USERS PLEASE READ
+//////////////////////////////////
+// This is just the beginning of LLVector4a. There are many more useful functions
+// yet to be implemented. For example, setNeg to negate a vector, rotate() to apply
+// a matrix rotation, various functions to manipulate only the X, Y, and Z elements
+// and many others (including a whole variety of accessors). So if you don't see a 
+// function here that you need, please contact Falcon or someone else with SSE 
+// experience (Richard, I think, has some and davep has a little as of the time 
+// of this writing, July 08, 2010) about getting it implemented before you resort to
+// LLVector3/LLVector4. 
+/////////////////////////////////
+
+class LLVector4a
+{
+public:
+
+	///////////////////////////////////
+	// STATIC METHODS
+	///////////////////////////////////
+	
+	// Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers
+	static void initClass()
+	{
+		_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+		_MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST);
+	}
+
+	// Return a vector of all zeros
+	static inline const LLVector4a& getZero()
+	{
+		extern const LLVector4a LL_V4A_ZERO;
+		return LL_V4A_ZERO;
+	}
+	
+	// Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks
+	static inline const LLVector4a& getEpsilon()
+	{
+		extern const LLVector4a LL_V4A_EPSILON;
+		return LL_V4A_EPSILON;
+	}
+
+	// Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned
+	static inline void copy4a(F32* dst, const F32* src)
+	{
+		_mm_store_ps(dst, _mm_load_ps(src));
+	}
+
+	// Copy words 16-byte blocks from src to dst. Source and destination must not overlap. 
+	static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes);
+
+	////////////////////////////////////
+	// CONSTRUCTORS 
+	////////////////////////////////////
+	
+	LLVector4a()
+	{ //DO NOT INITIALIZE -- The overhead is completely unnecessary
+	}
+	
+	LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f)
+	{
+		set(x,y,z,w);
+	}
+	
+	LLVector4a(F32 x)
+	{
+		splat(x);
+	}
+	
+	LLVector4a(const LLSimdScalar& x)
+	{
+		splat(x);
+	}
+
+	LLVector4a(LLQuad q)
+	{
+		mQ = q;
+	}
+
+	////////////////////////////////////
+	// LOAD/STORE
+	////////////////////////////////////
+	
+	// Load from 16-byte aligned src array (preferred method of loading)
+	inline void load4a(const F32* src);
+	
+	// Load from unaligned src array (NB: Significantly slower than load4a)
+	inline void loadua(const F32* src);
+	
+	// Load only three floats beginning at address 'src'. Slowest method.
+	inline void load3(const F32* src);
+	
+	// Store to a 16-byte aligned memory address
+	inline void store4a(F32* dst) const;
+	
+	////////////////////////////////////
+	// BASIC GET/SET 
+	////////////////////////////////////
+	
+	// Return a "this" as an F32 pointer. Do not use unless you have a very good reason.  (Not sure? Ask Falcon)
+	inline F32* getF32ptr();
+	
+	// Return a "this" as a const F32 pointer. Do not use unless you have a very good reason.  (Not sure? Ask Falcon)
+	inline const F32* const getF32ptr() const;
+	
+	// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates
+	// the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead
+	inline F32 operator[](const S32 idx) const;
+
+	// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+	inline LLSimdScalar getScalarAt(const S32 idx) const;
+
+	// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+	template <int N> LL_FORCE_INLINE LLSimdScalar getScalarAt() const;
+
+	// Set to an x, y, z and optional w provided
+	inline void set(F32 x, F32 y, F32 z, F32 w = 0.f);
+	
+	// Set to all zeros. This is preferred to using ::getZero()
+	inline void clear();
+	
+	// Set all elements to 'x'
+	inline void splat(const F32 x);
+
+	// Set all elements to 'x'
+	inline void splat(const LLSimdScalar& x);
+	
+	// Set all 4 elements to element N of src, with N known at compile time
+	template <int N> void splat(const LLVector4a& src);
+	
+	// Set all 4 elements to element i of v, with i NOT known at compile time
+	inline void splat(const LLVector4a& v, U32 i);
+	
+	// Select bits from sourceIfTrue and sourceIfFalse according to bits in mask
+	inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse );
+	
+	////////////////////////////////////
+	// ALGEBRAIC
+	////////////////////////////////////
+	
+	// Set this to the element-wise (a + b)
+	inline void setAdd(const LLVector4a& a, const LLVector4a& b);
+	
+	// Set this to element-wise (a - b)
+	inline void setSub(const LLVector4a& a, const LLVector4a& b);
+	
+	// Set this to element-wise multiply (a * b)
+	inline void setMul(const LLVector4a& a, const LLVector4a& b);
+	
+	// Set this to element-wise quotient (a / b)
+	inline void setDiv(const LLVector4a& a, const LLVector4a& b);
+	
+	// Set this to the element-wise absolute value of src
+	inline void setAbs(const LLVector4a& src);
+	
+	// Add to each component in this vector the corresponding component in rhs
+	inline void add(const LLVector4a& rhs);
+	
+	// Subtract from each component in this vector the corresponding component in rhs
+	inline void sub(const LLVector4a& rhs);
+	
+	// Multiply each component in this vector by the corresponding component in rhs
+	inline void mul(const LLVector4a& rhs);
+	
+	// Divide each component in this vector by the corresponding component in rhs
+	inline void div(const LLVector4a& rhs);
+	
+	// Multiply this vector by x in a scalar fashion
+	inline void mul(const F32 x);
+
+	// Set this to (a x b) (geometric cross-product)
+	inline void setCross3(const LLVector4a& a, const LLVector4a& b);
+	
+	// Set all elements to the dot product of the x, y, and z elements in a and b
+	inline void setAllDot3(const LLVector4a& a, const LLVector4a& b);
+
+	// Set all elements to the dot product of the x, y, z, and w elements in a and b
+	inline void setAllDot4(const LLVector4a& a, const LLVector4a& b);
+
+	// Return the 3D dot product of this vector and b
+	inline LLSimdScalar dot3(const LLVector4a& b) const;
+
+	// Return the 4D dot product of this vector and b
+	inline LLSimdScalar dot4(const LLVector4a& b) const;
+
+	// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
+	// Note that this does not consider zero length vectors!
+	inline void normalize3();
+
+	// Same as normalize3() but with respect to all 4 components
+	inline void normalize4();
+
+	// Same as normalize3(), but returns length as a SIMD scalar
+	inline LLSimdScalar normalize3withLength();
+
+	// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
+	// Note that this does not consider zero length vectors!
+	inline void normalize3fast();
+
+	// Return true if this vector is normalized with respect to x,y,z up to tolerance
+	inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const;
+
+	// Return true if this vector is normalized with respect to all components up to tolerance
+	inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const;
+
+	// Set all elements to the length of vector 'v' 
+	inline void setAllLength3( const LLVector4a& v );
+
+	// Get this vector's length
+	inline LLSimdScalar getLength3() const;
+	
+	// Set the components of this vector to the minimum of the corresponding components of lhs and rhs
+	inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs);
+	
+	// Set the components of this vector to the maximum of the corresponding components of lhs and rhs
+	inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs);
+	
+	// Clamps this vector to be within the component-wise range low to high (inclusive)
+	inline void clamp( const LLVector4a& low, const LLVector4a& high );
+
+	// Set this to  (c * lhs) + rhs * ( 1 - c)
+	inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c);
+	
+	// Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats
+	inline LLBool32 isFinite3() const;	
+	inline LLBool32 isFinite4() const;
+
+	// Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided
+	void setRotated( const LLRotation& rot, const LLVector4a& vec );
+	void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec );
+
+	// Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided
+	inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec );
+	inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec );
+
+	// Quantize this vector to 8 or 16 bit precision
+	void quantize8( const LLVector4a& low, const LLVector4a& high );
+	void quantize16( const LLVector4a& low, const LLVector4a& high );
+
+	////////////////////////////////////
+	// LOGICAL
+	////////////////////////////////////	
+	// The functions in this section will compare the elements in this vector
+	// to those in rhs and return an LLVector4Logical with all bits set in elements
+	// where the comparison was true and all bits unset in elements where the comparison
+	// was false. See llvector4logica.h
+	////////////////////////////////////
+	// WARNING: Other than equals3 and equals4, these functions do NOT account
+	// for floating point tolerance. You should include the appropriate tolerance
+	// in the inputs.
+	////////////////////////////////////
+	
+	inline LLVector4Logical greaterThan(const LLVector4a& rhs) const;
+
+	inline LLVector4Logical lessThan(const LLVector4a& rhs) const;
+	
+	inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const;
+
+	inline LLVector4Logical lessEqual(const LLVector4a& rhs) const;
+	
+	inline LLVector4Logical equal(const LLVector4a& rhs) const;
+
+	// Returns true if this and rhs are componentwise equal up to the specified absolute tolerance
+	inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+	inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+	////////////////////////////////////
+	// OPERATORS
+	////////////////////////////////////	
+	
+	// Do NOT add aditional operators without consulting someone with SSE experience
+	inline const LLVector4a& operator= ( const LLVector4a& rhs );
+	
+	inline const LLVector4a& operator= ( const LLQuad& rhs );
+
+	inline operator LLQuad() const;	
+
+private:
+	LLQuad mQ;
+};
+
+inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p)
+{
+	min.setMin(min, p);
+	max.setMax(max, p);
+}
+
+#endif
diff --git a/indra/llmath/llvector4a.inl b/indra/llmath/llvector4a.inl
new file mode 100644
index 0000000000000000000000000000000000000000..7ad22a563156dcbd1845816d2d97aff9ead85471
--- /dev/null
+++ b/indra/llmath/llvector4a.inl
@@ -0,0 +1,593 @@
+/** 
+ * @file llvector4a.inl
+ * @brief LLVector4a inline function implementations
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+////////////////////////////////////
+// LOAD/STORE
+////////////////////////////////////
+
+// Load from 16-byte aligned src array (preferred method of loading)
+inline void LLVector4a::load4a(const F32* src)
+{
+	mQ = _mm_load_ps(src);
+}
+
+// Load from unaligned src array (NB: Significantly slower than load4a)
+inline void LLVector4a::loadua(const F32* src)
+{
+	mQ = _mm_loadu_ps(src);
+}
+
+// Load only three floats beginning at address 'src'. Slowest method.
+inline void LLVector4a::load3(const F32* src)
+{
+	// mQ = { 0.f, src[2], src[1], src[0] } = { W, Z, Y, X }
+	// NB: This differs from the convention of { Z, Y, X, W }
+	mQ = _mm_set_ps(0.f, src[2], src[1], src[0]);
+}	
+
+// Store to a 16-byte aligned memory address
+inline void LLVector4a::store4a(F32* dst) const
+{
+	_mm_store_ps(dst, mQ);
+}
+
+////////////////////////////////////
+// BASIC GET/SET 
+////////////////////////////////////
+
+// Return a "this" as an F32 pointer. Do not use unless you have a very good reason.  (Not sure? Ask Falcon)
+F32* LLVector4a::getF32ptr()
+{
+	return (F32*) &mQ;
+}
+
+// Return a "this" as a const F32 pointer. Do not use unless you have a very good reason.  (Not sure? Ask Falcon)
+const F32* const LLVector4a::getF32ptr() const
+{
+	return (const F32* const) &mQ;
+}
+
+// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates
+// the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead
+inline F32 LLVector4a::operator[](const S32 idx) const
+{
+	return ((F32*)&mQ)[idx];
+}	
+
+// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+inline LLSimdScalar LLVector4a::getScalarAt(const S32 idx) const
+{
+	// Return appropriate LLQuad. It will be cast to LLSimdScalar automatically (should be effectively a nop)
+	switch (idx)
+	{
+		case 0:
+			return mQ;
+		case 1:
+			return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(1, 1, 1, 1));
+		case 2:
+			return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(2, 2, 2, 2));
+		case 3:
+		default:
+			return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(3, 3, 3, 3));
+	}
+}
+
+// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+template <int N> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt() const
+{
+	return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(N, N, N, N));
+}
+
+template<> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt<0>() const
+{
+	return mQ;
+}
+
+// Set to an x, y, z and optional w provided
+inline void LLVector4a::set(F32 x, F32 y, F32 z, F32 w)
+{
+	mQ = _mm_set_ps(w, z, y, x);
+}
+
+// Set to all zeros
+inline void LLVector4a::clear()
+{
+	mQ = LLVector4a::getZero().mQ;
+}
+
+inline void LLVector4a::splat(const F32 x)
+{
+	mQ = _mm_set1_ps(x);	
+}
+
+inline void LLVector4a::splat(const LLSimdScalar& x)
+{
+	mQ = _mm_shuffle_ps( x.getQuad(), x.getQuad(), _MM_SHUFFLE(0,0,0,0) );
+}
+
+// Set all 4 elements to element N of src, with N known at compile time
+template <int N> void LLVector4a::splat(const LLVector4a& src)
+{
+	mQ = _mm_shuffle_ps(src.mQ, src.mQ, _MM_SHUFFLE(N, N, N, N) );
+}
+
+// Set all 4 elements to element i of v, with i NOT known at compile time
+inline void LLVector4a::splat(const LLVector4a& v, U32 i)
+{
+	switch (i)
+	{
+		case 0:
+			mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(0, 0, 0, 0));
+			break;
+		case 1:
+			mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(1, 1, 1, 1));
+			break;
+		case 2:
+			mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(2, 2, 2, 2));
+			break;
+		case 3:
+			mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(3, 3, 3, 3));
+			break;
+	}
+}
+
+// Select bits from sourceIfTrue and sourceIfFalse according to bits in mask
+inline void LLVector4a::setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse )
+{
+	// ((( sourceIfTrue ^ sourceIfFalse ) & mask) ^ sourceIfFalse )
+	// E.g., sourceIfFalse = 1010b, sourceIfTrue = 0101b, mask = 1100b
+	// (sourceIfTrue ^ sourceIfFalse) = 1111b --> & mask = 1100b --> ^ sourceIfFalse = 0110b, 
+	// as expected (01 from sourceIfTrue, 10 from sourceIfFalse)
+	// Courtesy of Mark++, http://markplusplus.wordpress.com/2007/03/14/fast-sse-select-operation/
+	mQ = _mm_xor_ps( sourceIfFalse, _mm_and_ps( mask, _mm_xor_ps( sourceIfTrue, sourceIfFalse ) ) );
+}
+
+////////////////////////////////////
+// ALGEBRAIC
+////////////////////////////////////
+
+// Set this to the element-wise (a + b)
+inline void LLVector4a::setAdd(const LLVector4a& a, const LLVector4a& b)
+{
+	mQ = _mm_add_ps(a.mQ, b.mQ);
+}
+
+// Set this to element-wise (a - b)
+inline void LLVector4a::setSub(const LLVector4a& a, const LLVector4a& b)
+{
+	mQ = _mm_sub_ps(a.mQ, b.mQ);
+}
+
+// Set this to element-wise multiply (a * b)
+inline void LLVector4a::setMul(const LLVector4a& a, const LLVector4a& b)
+{
+	mQ = _mm_mul_ps(a.mQ, b.mQ);
+}
+
+// Set this to element-wise quotient (a / b)
+inline void LLVector4a::setDiv(const LLVector4a& a, const LLVector4a& b)
+{
+	mQ = _mm_div_ps( a.mQ, b.mQ );
+}
+
+// Set this to the element-wise absolute value of src
+inline void LLVector4a::setAbs(const LLVector4a& src)
+{
+	static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
+	mQ = _mm_and_ps(src.mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A));
+}
+
+// Add to each component in this vector the corresponding component in rhs
+inline void LLVector4a::add(const LLVector4a& rhs)
+{
+	mQ = _mm_add_ps(mQ, rhs.mQ);	
+}
+
+// Subtract from each component in this vector the corresponding component in rhs
+inline void LLVector4a::sub(const LLVector4a& rhs)
+{
+	mQ = _mm_sub_ps(mQ, rhs.mQ);
+}
+
+// Multiply each component in this vector by the corresponding component in rhs
+inline void LLVector4a::mul(const LLVector4a& rhs)
+{
+	mQ = _mm_mul_ps(mQ, rhs.mQ);	
+}
+
+// Divide each component in this vector by the corresponding component in rhs
+inline void LLVector4a::div(const LLVector4a& rhs)
+{
+	// TODO: Check accuracy, maybe add divFast
+	mQ = _mm_div_ps(mQ, rhs.mQ);
+}
+
+// Multiply this vector by x in a scalar fashion
+inline void LLVector4a::mul(const F32 x) 
+{
+	LLVector4a t;
+	t.splat(x);
+	
+	mQ = _mm_mul_ps(mQ, t.mQ);
+}
+
+// Set this to (a x b) (geometric cross-product)
+inline void LLVector4a::setCross3(const LLVector4a& a, const LLVector4a& b)
+{
+	// Vectors are stored in memory in w, z, y, x order from high to low
+	// Set vector1 = { a[W], a[X], a[Z], a[Y] }
+	const LLQuad vector1 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
+	// Set vector2 = { b[W], b[Y], b[X], b[Z] }
+	const LLQuad vector2 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
+	// mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] }
+	mQ = _mm_mul_ps( vector1, vector2 );
+	// vector3 = { a[W], a[Y], a[X], a[Z] }
+	const LLQuad vector3 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
+	// vector4 = { b[W], b[X], b[Z], b[Y] }
+	const LLQuad vector4 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
+	// mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] }
+	mQ = _mm_sub_ps( mQ, _mm_mul_ps( vector3, vector4 ));
+}
+
+/* This function works, but may be slightly slower than the one below on older machines
+ inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b)
+ {
+ // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] }
+ const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ );
+ // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] }
+ const LLQuad wzxy = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE(3, 2, 0, 1 ));
+ // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad xPlusY = _mm_add_ps(ab, wzxy);
+ // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } 
+ const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY);
+ // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] }
+ const LLQuad zSplat = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE( 2, 2, 2, 2 ));
+ // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same }
+ mQ = _mm_add_ps(zSplat, xPlusYSplat);
+ }*/
+
+// Set all elements to the dot product of the x, y, and z elements in a and b
+inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b)
+{
+	// ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] }
+	const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ );
+	// yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] }
+	const __m128i wzxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(3, 2, 0, 1 ));
+	// xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+	const LLQuad xPlusY = _mm_add_ps(ab, _mm_castsi128_ps(wzxy));
+	// xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } 
+	const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY);
+	// zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] }
+	const __m128i zSplat = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE( 2, 2, 2, 2 ));
+	// mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same }
+	mQ = _mm_add_ps(_mm_castsi128_ps(zSplat), xPlusYSplat);
+}
+
+// Set all elements to the dot product of the x, y, z, and w elements in a and b
+inline void LLVector4a::setAllDot4(const LLVector4a& a, const LLVector4a& b)
+{
+	// ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] }
+	const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ );
+	// yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] }
+	const __m128i zwxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(2, 3, 0, 1 ));
+	// zPlusWandXplusY = { a[W]*b[W] + a[Z]*b[Z], a[Z] * b[Z] + a[W]*b[W], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+	const LLQuad zPlusWandXplusY = _mm_add_ps(ab, _mm_castsi128_ps(zwxy));
+	// xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } 
+	const LLQuad xPlusYSplat = _mm_movelh_ps(zPlusWandXplusY, zPlusWandXplusY);
+	const LLQuad zPlusWSplat = _mm_movehl_ps(zPlusWandXplusY, zPlusWandXplusY);
+
+	// mQ = { a[W]*b[W] + a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same }
+	mQ = _mm_add_ps(xPlusYSplat, zPlusWSplat);
+}
+
+// Return the 3D dot product of this vector and b
+inline LLSimdScalar LLVector4a::dot3(const LLVector4a& b) const
+{
+	const LLQuad ab = _mm_mul_ps( mQ, b.mQ );
+	const LLQuad splatY = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(1, 1, 1, 1) ) );
+	const LLQuad splatZ = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(2, 2, 2, 2) ) );
+	const LLQuad xPlusY = _mm_add_ps( ab, splatY );
+	return _mm_add_ps( xPlusY, splatZ );	
+}
+
+// Return the 4D dot product of this vector and b
+inline LLSimdScalar LLVector4a::dot4(const LLVector4a& b) const
+{
+	// ab = { w, z, y, x }
+ 	const LLQuad ab = _mm_mul_ps( mQ, b.mQ );
+ 	// upperProdsInLowerElems = { y, x, y, x }
+	const LLQuad upperProdsInLowerElems = _mm_movehl_ps( ab, ab );
+	// sumOfPairs = { w+y, z+x, 2y, 2x }
+ 	const LLQuad sumOfPairs = _mm_add_ps( upperProdsInLowerElems, ab );
+	// shuffled = { z+x, z+x, z+x, z+x }
+	const LLQuad shuffled = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( sumOfPairs ), _MM_SHUFFLE(1, 1, 1, 1) ) );
+	return _mm_add_ss( sumOfPairs, shuffled );
+}
+
+// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
+// Note that this does not consider zero length vectors!
+inline void LLVector4a::normalize3()
+{
+	// lenSqrd = a dot a
+	LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
+	// rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 }
+	const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+	static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f };
+	static const LLQuad three = {3.f, 3.f, 3.f, 3.f };
+	// Now we do one round of Newton-Raphson approximation to get full accuracy
+	// According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a))
+	// the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3))
+	// w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3
+	// = 0.5 * w * (3 - a*w^2)
+	// Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula
+	// which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)]
+	const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt );
+	const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt );
+	const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt );
+	const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt));
+	mQ = _mm_mul_ps( mQ, nrApprox );
+}
+
+// Normalize this vector with respect to all components. Accurate to 22 bites of precision.
+// Note that this does not consider zero length vectors!
+inline void LLVector4a::normalize4()
+{
+	// lenSqrd = a dot a
+	LLVector4a lenSqrd; lenSqrd.setAllDot4( *this, *this );
+	// rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 }
+	const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+	static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f };
+	static const LLQuad three = {3.f, 3.f, 3.f, 3.f };
+	// Now we do one round of Newton-Raphson approximation to get full accuracy
+	// According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a))
+	// the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3))
+	// w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3
+	// = 0.5 * w * (3 - a*w^2)
+	// Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula
+	// which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)]
+	const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt );
+	const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt );
+	const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt );
+	const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt));
+	mQ = _mm_mul_ps( mQ, nrApprox );
+}
+
+// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
+// Note that this does not consider zero length vectors!
+inline LLSimdScalar LLVector4a::normalize3withLength()
+{
+	// lenSqrd = a dot a
+	LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
+	// rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 }
+	const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+	static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f };
+	static const LLQuad three = {3.f, 3.f, 3.f, 3.f };
+	// Now we do one round of Newton-Raphson approximation to get full accuracy
+	// According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a))
+	// the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3))
+	// w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3
+	// = 0.5 * w * (3 - a*w^2)
+	// Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula
+	// which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)]
+	const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt );
+	const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt );
+	const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt );
+	const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt));
+	mQ = _mm_mul_ps( mQ, nrApprox );
+	return _mm_sqrt_ss(lenSqrd);
+}
+
+// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
+// Note that this does not consider zero length vectors!
+inline void LLVector4a::normalize3fast()
+{
+	LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
+	const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+	mQ = _mm_mul_ps( mQ, approxRsqrt );
+}
+
+// Return true if this vector is normalized with respect to x,y,z up to tolerance
+inline LLBool32 LLVector4a::isNormalized3( F32 tolerance ) const
+{
+	static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 };
+	LLSimdScalar tol = _mm_load_ss( &tolerance );
+	tol = _mm_mul_ss( tol, tol );
+	LLVector4a lenSquared; lenSquared.setAllDot3( *this, *this );
+	lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) );
+	lenSquared.setAbs(lenSquared);
+	return _mm_comile_ss( lenSquared, tol );		
+}
+
+// Return true if this vector is normalized with respect to all components up to tolerance
+inline LLBool32 LLVector4a::isNormalized4( F32 tolerance ) const
+{
+	static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 };
+	LLSimdScalar tol = _mm_load_ss( &tolerance );
+	tol = _mm_mul_ss( tol, tol );
+	LLVector4a lenSquared; lenSquared.setAllDot4( *this, *this );
+	lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) );
+	lenSquared.setAbs(lenSquared);
+	return _mm_comile_ss( lenSquared, tol );		
+}
+
+// Set all elements to the length of vector 'v' 
+inline void LLVector4a::setAllLength3( const LLVector4a& v )
+{
+	LLVector4a lenSqrd;
+	lenSqrd.setAllDot3(v, v);
+	
+	mQ = _mm_sqrt_ps(lenSqrd.mQ);
+}
+
+// Get this vector's length
+inline LLSimdScalar LLVector4a::getLength3() const
+{
+	return _mm_sqrt_ss( dot3( (const LLVector4a)mQ ) );
+}
+
+// Set the components of this vector to the minimum of the corresponding components of lhs and rhs
+inline void LLVector4a::setMin(const LLVector4a& lhs, const LLVector4a& rhs)
+{
+	mQ = _mm_min_ps(lhs.mQ, rhs.mQ);
+}
+
+// Set the components of this vector to the maximum of the corresponding components of lhs and rhs
+inline void LLVector4a::setMax(const LLVector4a& lhs, const LLVector4a& rhs)
+{
+	mQ = _mm_max_ps(lhs.mQ, rhs.mQ);
+}
+
+// Set this to  (c * lhs) + rhs * ( 1 - c)
+inline void LLVector4a::setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c)
+{
+	LLVector4a a = lhs;
+	a.mul(c);
+	
+	LLVector4a b = rhs;
+	b.mul(1.f-c);
+	
+	setAdd(a, b);
+}
+
+inline LLBool32 LLVector4a::isFinite3() const
+{
+	static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 };
+	const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask);
+	const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV );
+	const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV ));
+	return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZ );
+}
+	
+inline LLBool32 LLVector4a::isFinite4() const
+{
+	static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 };
+	const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask);
+	const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV );
+	const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV ));
+	return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZW );
+}
+
+inline void LLVector4a::setRotatedInv( const LLRotation& rot, const LLVector4a& vec )
+{
+	LLRotation inv; inv.setTranspose( rot );
+	setRotated( inv, vec );
+}
+
+inline void LLVector4a::setRotatedInv( const LLQuaternion2& quat, const LLVector4a& vec )
+{
+	LLQuaternion2 invRot; invRot.setConjugate( quat );
+	setRotated(invRot, vec);
+}
+
+inline void LLVector4a::clamp( const LLVector4a& low, const LLVector4a& high )
+{
+	const LLVector4Logical highMask = greaterThan( high );
+	const LLVector4Logical lowMask = lessThan( low );
+
+	setSelectWithMask( highMask, high, *this );
+	setSelectWithMask( lowMask, low, *this );
+}
+
+
+////////////////////////////////////
+// LOGICAL
+////////////////////////////////////	
+// The functions in this section will compare the elements in this vector
+// to those in rhs and return an LLVector4Logical with all bits set in elements
+// where the comparison was true and all bits unset in elements where the comparison
+// was false. See llvector4logica.h
+////////////////////////////////////
+// WARNING: Other than equals3 and equals4, these functions do NOT account
+// for floating point tolerance. You should include the appropriate tolerance
+// in the inputs.
+////////////////////////////////////
+
+inline LLVector4Logical LLVector4a::greaterThan(const LLVector4a& rhs) const
+{	
+	return _mm_cmpgt_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::lessThan(const LLVector4a& rhs) const
+{
+	return _mm_cmplt_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::greaterEqual(const LLVector4a& rhs) const
+{
+	return _mm_cmpge_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::lessEqual(const LLVector4a& rhs) const
+{
+	return _mm_cmple_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::equal(const LLVector4a& rhs) const
+{
+	return _mm_cmpeq_ps(mQ, rhs.mQ);
+}
+
+// Returns true if this and rhs are componentwise equal up to the specified absolute tolerance
+inline bool LLVector4a::equals4(const LLVector4a& rhs, F32 tolerance ) const
+{
+	LLVector4a diff; diff.setSub( *this, rhs );
+	diff.setAbs( diff );
+	const LLQuad tol = _mm_set1_ps( tolerance );
+	const LLQuad cmp = _mm_cmplt_ps( diff, tol );
+	return (_mm_movemask_ps( cmp ) & LLVector4Logical::MASK_XYZW) == LLVector4Logical::MASK_XYZW;
+}
+
+inline bool LLVector4a::equals3(const LLVector4a& rhs, F32 tolerance ) const
+{
+	LLVector4a diff; diff.setSub( *this, rhs );
+	diff.setAbs( diff );
+	const LLQuad tol = _mm_set1_ps( tolerance );
+	const LLQuad t = _mm_cmplt_ps( diff, tol ); 
+	return (_mm_movemask_ps( t ) & LLVector4Logical::MASK_XYZ) == LLVector4Logical::MASK_XYZ;
+	
+}
+
+////////////////////////////////////
+// OPERATORS
+////////////////////////////////////	
+
+// Do NOT add aditional operators without consulting someone with SSE experience
+inline const LLVector4a& LLVector4a::operator= ( const LLVector4a& rhs )
+{
+	mQ = rhs.mQ;
+	return *this;
+}
+
+inline const LLVector4a& LLVector4a::operator= ( const LLQuad& rhs )
+{
+	mQ = rhs;
+	return *this;
+}
+
+inline LLVector4a::operator LLQuad() const
+{
+	return mQ;
+}
diff --git a/indra/llmath/llvector4logical.h b/indra/llmath/llvector4logical.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd66b09d4391608eda6e4affa0674b4825cc288b
--- /dev/null
+++ b/indra/llmath/llvector4logical.h
@@ -0,0 +1,124 @@
+/** 
+ * @file llvector4logical.h
+ * @brief LLVector4Logical class header file - Companion class to LLVector4a for logical and bit-twiddling operations
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef	LL_VECTOR4LOGICAL_H
+#define	LL_VECTOR4LOGICAL_H
+
+
+////////////////////////////
+// LLVector4Logical
+////////////////////////////
+// This class is incomplete. If you need additional functionality,
+// for example setting/unsetting particular elements or performing
+// other boolean operations, feel free to implement. If you need
+// assistance in determining the most optimal implementation,
+// contact someone with SSE experience (Falcon, Richard, Davep, e.g.)
+////////////////////////////
+
+static LL_ALIGN_16(const U32 S_V4LOGICAL_MASK_TABLE[4*4]) =
+{
+	0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF
+};
+
+class LLVector4Logical
+{
+public:
+	
+	enum {
+		MASK_X = 1,
+		MASK_Y = 1 << 1,
+		MASK_Z = 1 << 2,
+		MASK_W = 1 << 3,
+		MASK_XYZ = MASK_X | MASK_Y | MASK_Z,
+		MASK_XYZW = MASK_XYZ | MASK_W
+	};
+	
+	// Empty default ctor
+	LLVector4Logical() {}
+	
+	LLVector4Logical( const LLQuad& quad )
+	{
+		mQ = quad;
+	}
+	
+	// Create and return a mask consisting of the lowest order bit of each element
+	inline U32 getGatheredBits() const
+	{
+		return _mm_movemask_ps(mQ);
+	};	
+	
+	// Invert this mask
+	inline LLVector4Logical& invert()
+	{
+		static const LL_ALIGN_16(U32 allOnes[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+		mQ = _mm_andnot_ps( mQ, *(LLQuad*)(allOnes) );
+		return *this;
+	}
+	
+	inline LLBool32 areAllSet( U32 mask ) const
+	{
+		return ( getGatheredBits() & mask) == mask;
+	}
+	
+	inline LLBool32 areAllSet() const
+	{
+		return areAllSet( MASK_XYZW );
+	}
+		
+	inline LLBool32 areAnySet( U32 mask ) const
+	{
+		return getGatheredBits() & mask;
+	}
+	
+	inline LLBool32 areAnySet() const
+	{
+		return areAnySet( MASK_XYZW );
+	}
+	
+	inline operator LLQuad() const
+	{
+		return mQ;
+	}
+
+	inline void clear() 
+	{
+		mQ = _mm_setzero_ps();
+	}
+
+	template<int N> void setElement()
+	{
+		mQ = _mm_or_ps( mQ, *reinterpret_cast<const LLQuad*>(S_V4LOGICAL_MASK_TABLE + 4*N) );
+	}
+	
+private:
+	
+	LLQuad mQ;
+};
+
+#endif //LL_VECTOR4ALOGICAL_H
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 71b92962fb0fc93a20f8a51dcdcf47eead89d9d4..c504215ee5252f9c7ccf315395e4b721ef5b1bbe 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -1,4 +1,5 @@
 /** 
+
  * @file llvolume.cpp
  *
  * $LicenseInfo:firstyear=2002&license=viewerlgpl$
@@ -24,9 +25,13 @@
  */
 
 #include "linden_common.h"
+#include "llmemory.h"
 #include "llmath.h"
 
 #include <set>
+#if !LL_WINDOWS
+#include <stdint.h>
+#endif
 
 #include "llerror.h"
 #include "llmemtype.h"
@@ -37,9 +42,16 @@
 #include "v4math.h"
 #include "m4math.h"
 #include "m3math.h"
+#include "llmatrix3a.h"
+#include "lloctree.h"
 #include "lldarray.h"
 #include "llvolume.h"
+#include "llvolumeoctree.h"
 #include "llstl.h"
+#include "llsdserialize.h"
+#include "llvector4a.h"
+#include "llmatrix4a.h"
+#include "lltimer.h"
 
 #define DEBUG_SILHOUETTE_BINORMALS 0
 #define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
@@ -80,7 +92,18 @@ const F32 SKEW_MAX	=  0.95f;
 const F32 SCULPT_MIN_AREA = 0.002f;
 const S32 SCULPT_MIN_AREA_DETAIL = 1;
 
-#define GEN_TRI_STRIP 0
+extern BOOL gDebugGL;
+
+void assert_aligned(void* ptr, uintptr_t alignment)
+{
+#if 0
+	uintptr_t t = (uintptr_t) ptr;
+	if (t%alignment != 0)
+	{
+		llerrs << "WTF?" << llendl;
+	}
+#endif
+}
 
 BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
 {    
@@ -99,128 +122,262 @@ BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLV
 
 BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
 {
-	float fAWdU[3];
-	LLVector3 dir;
-	LLVector3 diff;
+	return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV);
+}
+
+BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size)
+{
+	F32 fAWdU[3];
+	F32 dir[3];
+	F32 diff[3];
 
 	for (U32 i = 0; i < 3; i++)
 	{
-		dir.mV[i] = 0.5f * (end.mV[i] - start.mV[i]);
-		diff.mV[i] = (0.5f * (end.mV[i] + start.mV[i])) - center.mV[i];
-		fAWdU[i] = fabsf(dir.mV[i]);
-		if(fabsf(diff.mV[i])>size.mV[i] + fAWdU[i]) return false;
+		dir[i] = 0.5f * (end[i] - start[i]);
+		diff[i] = (0.5f * (end[i] + start[i])) - center[i];
+		fAWdU[i] = fabsf(dir[i]);
+		if(fabsf(diff[i])>size[i] + fAWdU[i]) return false;
 	}
 
 	float f;
-	f = dir.mV[1] * diff.mV[2] - dir.mV[2] * diff.mV[1];    if(fabsf(f)>size.mV[1]*fAWdU[2] + size.mV[2]*fAWdU[1])  return false;
-	f = dir.mV[2] * diff.mV[0] - dir.mV[0] * diff.mV[2];    if(fabsf(f)>size.mV[0]*fAWdU[2] + size.mV[2]*fAWdU[0])  return false;
-	f = dir.mV[0] * diff.mV[1] - dir.mV[1] * diff.mV[0];    if(fabsf(f)>size.mV[0]*fAWdU[1] + size.mV[1]*fAWdU[0])  return false;
+	f = dir[1] * diff[2] - dir[2] * diff[1];    if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1])  return false;
+	f = dir[2] * diff[0] - dir[0] * diff[2];    if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0])  return false;
+	f = dir[0] * diff[1] - dir[1] * diff[0];    if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0])  return false;
 	
 	return true;
 }
 
 
+
 // intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
 // returns TRUE if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
 // and returns the intersection point along dir in intersection_t.
 
 // Moller-Trumbore algorithm
-BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
-							F32* intersection_a, F32* intersection_b, F32* intersection_t, BOOL two_sided)
+BOOL LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+							F32& intersection_a, F32& intersection_b, F32& intersection_t)
 {
-	F32 u, v, t;
 	
 	/* find vectors for two edges sharing vert0 */
-	LLVector3 edge1 = vert1 - vert0;
+	LLVector4a edge1;
+	edge1.setSub(vert1, vert0);
 	
-	LLVector3 edge2 = vert2 - vert0;;
+	LLVector4a edge2;
+	edge2.setSub(vert2, vert0);
 
 	/* begin calculating determinant - also used to calculate U parameter */
-	LLVector3 pvec = dir % edge2;
-	
-	/* if determinant is near zero, ray lies in plane of triangle */
-	F32 det = edge1 * pvec;
+	LLVector4a pvec;
+	pvec.setCross3(dir, edge2);
 
-	if (!two_sided)
+	/* if determinant is near zero, ray lies in plane of triangle */
+	LLVector4a det;
+	det.setAllDot3(edge1, pvec);
+	
+	if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7)
 	{
-		if (det < F_APPROXIMATELY_ZERO)
-		{
-			return FALSE;
-		}
-
 		/* calculate distance from vert0 to ray origin */
-		LLVector3 tvec = orig - vert0;
+		LLVector4a tvec;
+		tvec.setSub(orig, vert0);
 
 		/* calculate U parameter and test bounds */
-		u = tvec * pvec;	
+		LLVector4a u;
+		u.setAllDot3(tvec,pvec);
 
-		if (u < 0.f || u > det)
+		if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) &&
+			(u.lessEqual(det).getGatheredBits() & 0x7))
 		{
-			return FALSE;
+			/* prepare to test V parameter */
+			LLVector4a qvec;
+			qvec.setCross3(tvec, edge1);
+			
+			/* calculate V parameter and test bounds */
+			LLVector4a v;
+			v.setAllDot3(dir, qvec);
+
+			
+			//if (!(v < 0.f || u + v > det))
+
+			LLVector4a sum_uv;
+			sum_uv.setAdd(u, v);
+
+			S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7;
+			S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7;
+
+			if (v_gequal  && sum_lequal)
+			{
+				/* calculate t, scale parameters, ray intersects triangle */
+				LLVector4a t;
+				t.setAllDot3(edge2,qvec);
+
+				t.div(det);
+				u.div(det);
+				v.div(det);
+				
+				intersection_a = u[0];
+				intersection_b = v[0];
+				intersection_t = t[0];
+				return TRUE;
+			}
 		}
-	
-		/* prepare to test V parameter */
-		LLVector3 qvec = tvec % edge1;
+	}
 		
-		/* calculate V parameter and test bounds */
-		v = dir * qvec;
-		if (v < 0.f || u + v > det)
-		{
-			return FALSE;
-		}
+	return FALSE;
+} 
 
-		/* calculate t, scale parameters, ray intersects triangle */
-		t = edge2 * qvec;
-		F32 inv_det = 1.0 / det;
-		t *= inv_det;
-		u *= inv_det;
-		v *= inv_det;
-	}
+BOOL LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+							F32& intersection_a, F32& intersection_b, F32& intersection_t)
+{
+	F32 u, v, t;
 	
-	else // two sided
-			{
-		if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
-				{
-			return FALSE;
-				}
-		F32 inv_det = 1.0 / det;
+	/* find vectors for two edges sharing vert0 */
+	LLVector4a edge1;
+	edge1.setSub(vert1, vert0);
+	
+	
+	LLVector4a edge2;
+	edge2.setSub(vert2, vert0);
 
-		/* calculate distance from vert0 to ray origin */
-		LLVector3 tvec = orig - vert0;
-		
-		/* calculate U parameter and test bounds */
-		u = (tvec * pvec) * inv_det;
-		if (u < 0.f || u > 1.f)
-		{
-			return FALSE;
-			}
+	/* begin calculating determinant - also used to calculate U parameter */
+	LLVector4a pvec;
+	pvec.setCross3(dir, edge2);
 
-		/* prepare to test V parameter */
-		LLVector3 qvec = tvec - edge1;
-		
-		/* calculate V parameter and test bounds */
-		v = (dir * qvec) * inv_det;
-		
-		if (v < 0.f || u + v > 1.f)
-		{
-			return FALSE;
-		}
+	/* if determinant is near zero, ray lies in plane of triangle */
+	F32 det = edge1.dot3(pvec).getF32();
 
-		/* calculate t, ray intersects triangle */
-		t = (edge2 * qvec) * inv_det;
+	
+	if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
+	{
+		return FALSE;
+	}
+
+	F32 inv_det = 1.f / det;
+
+	/* calculate distance from vert0 to ray origin */
+	LLVector4a tvec;
+	tvec.setSub(orig, vert0);
+	
+	/* calculate U parameter and test bounds */
+	u = (tvec.dot3(pvec).getF32()) * inv_det;
+	if (u < 0.f || u > 1.f)
+	{
+		return FALSE;
+	}
+
+	/* prepare to test V parameter */
+	tvec.sub(edge1);
+		
+	/* calculate V parameter and test bounds */
+	v = (dir.dot3(tvec).getF32()) * inv_det;
+	
+	if (v < 0.f || u + v > 1.f)
+	{
+		return FALSE;
 	}
+
+	/* calculate t, ray intersects triangle */
+	t = (edge2.dot3(tvec).getF32()) * inv_det;
 	
-	if (intersection_a != NULL)
-		*intersection_a = u;
-	if (intersection_b != NULL)
-		*intersection_b = v;
-	if (intersection_t != NULL)
-		*intersection_t = t;
+	intersection_a = u;
+	intersection_b = v;
+	intersection_t = t;
 	
 	
 	return TRUE;
 } 
 
+//helper for non-aligned vectors
+BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
+							F32& intersection_a, F32& intersection_b, F32& intersection_t, BOOL two_sided)
+{
+	LLVector4a vert0a, vert1a, vert2a, origa, dira;
+	vert0a.load3(vert0.mV);
+	vert1a.load3(vert1.mV);
+	vert2a.load3(vert2.mV);
+	origa.load3(orig.mV);
+	dira.load3(dir.mV);
+
+	if (two_sided)
+	{
+		return LLTriangleRayIntersectTwoSided(vert0a, vert1a, vert2a, origa, dira, 
+				intersection_a, intersection_b, intersection_t);
+	}
+	else
+	{
+		return LLTriangleRayIntersect(vert0a, vert1a, vert2a, origa, dira, 
+				intersection_a, intersection_b, intersection_t);
+	}
+}
+
+class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
+{
+public:
+	const LLVolumeFace* mFace;
+
+	LLVolumeOctreeRebound(const LLVolumeFace* face)
+	{
+		mFace = face;
+	}
+
+	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+	{ //this is a depth first traversal, so it's safe to assum all children have complete
+		//bounding data
+
+		LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
+
+		LLVector4a& min = node->mExtents[0];
+		LLVector4a& max = node->mExtents[1];
+
+		if (!branch->getData().empty())
+		{ //node has data, find AABB that binds data set
+			const LLVolumeTriangle* tri = *(branch->getData().begin());
+			
+			//initialize min/max to first available vertex
+			min = *(tri->mV[0]);
+			max = *(tri->mV[0]);
+			
+			for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = 
+				branch->getData().begin(); iter != branch->getData().end(); ++iter)
+			{ //for each triangle in node
+
+				//stretch by triangles in node
+				tri = *iter;
+				
+				min.setMin(min, *tri->mV[0]);
+				min.setMin(min, *tri->mV[1]);
+				min.setMin(min, *tri->mV[2]);
+
+				max.setMax(max, *tri->mV[0]);
+				max.setMax(max, *tri->mV[1]);
+				max.setMax(max, *tri->mV[2]);
+			}
+		}
+		else if (!branch->getChildren().empty())
+		{ //no data, but child nodes exist
+			LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
+
+			//initialize min/max to extents of first child
+			min = child->mExtents[0];
+			max = child->mExtents[1];
+		}
+		else
+		{
+			llerrs << "WTF? Empty leaf" << llendl;
+		}
+
+		for (S32 i = 0; i < branch->getChildCount(); ++i)
+		{  //stretch by child extents
+			LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
+			min.setMin(min, child->mExtents[0]);
+			max.setMax(max, child->mExtents[1]);
+		}
+
+		node->mBounds[0].setAdd(min, max);
+		node->mBounds[0].mul(0.5f);
+
+		node->mBounds[1].setSub(max,min);
+		node->mBounds[1].mul(0.5f);
+	}
+};
 
 //-------------------------------------------------------------------
 // statics
@@ -1669,7 +1826,13 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
 	mFaceMask = 0x0;
 	mDetail = detail;
 	mSculptLevel = -2;
-	
+	mIsTetrahedron = FALSE;
+	mLODScaleBias.setVec(1,1,1);
+	mHullPoints = NULL;
+	mHullIndices = NULL;
+	mNumHullPoints = 0;
+	mNumHullIndices = 0;
+
 	// set defaults
 	if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
 	{
@@ -1684,7 +1847,8 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
 	mGenerateSingleFace = generate_single_face;
 
 	generate();
-	if (mParams.getSculptID().isNull() && params.getSculptType() == LL_SCULPT_TYPE_NONE)
+	
+	if (mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE)
 	{
 		createVolumeFaces();
 	}
@@ -1719,6 +1883,11 @@ LLVolume::~LLVolume()
 	mPathp = NULL;
 	mProfilep = NULL;
 	mVolumeFaces.clear();
+
+	ll_aligned_free_16(mHullPoints);
+	mHullPoints = NULL;
+	ll_aligned_free_16(mHullIndices);
+	mHullIndices = NULL;
 }
 
 BOOL LLVolume::generate()
@@ -1835,869 +2004,1449 @@ BOOL LLVolume::generate()
 	return FALSE;
 }
 
-
-void LLVolume::createVolumeFaces()
+void LLVolumeFace::VertexData::init()
 {
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-
-	if (mGenerateSingleFace)
+	if (!mData)
 	{
-		// do nothing
+		mData = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*2);
 	}
-	else
-	{
-		S32 num_faces = getNumFaces();
-		BOOL partial_build = TRUE;
-		if (num_faces != mVolumeFaces.size())
-		{
-			partial_build = FALSE;
-			mVolumeFaces.resize(num_faces);
-		}
-		// Initialize volume faces with parameter data
-		for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++)
-		{
-			LLVolumeFace& vf = mVolumeFaces[i];
-			LLProfile::Face& face = mProfilep->mFaces[i];
-			vf.mBeginS = face.mIndex;
-			vf.mNumS = face.mCount;
-			if (vf.mNumS < 0)
-			{
-				llerrs << "Volume face corruption detected." << llendl;
-			}
-
-			vf.mBeginT = 0;
-			vf.mNumT= getPath().mPath.size();
-			vf.mID = i;
+}
 
-			// Set the type mask bits correctly
-			if (mParams.getProfileParams().getHollow() > 0)
-			{
-				vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
-			}
-			if (mProfilep->isOpen())
-			{
-				vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
-			}
-			if (face.mCap)
-			{
-				vf.mTypeMask |= LLVolumeFace::CAP_MASK;
-				if (face.mFaceID == LL_FACE_PATH_BEGIN)
-				{
-					vf.mTypeMask |= LLVolumeFace::TOP_MASK;
-				}
-				else
-				{
-					llassert(face.mFaceID == LL_FACE_PATH_END);
-					vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
-				}
-			}
-			else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
-			{
-				vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
-			}
-			else
-			{
-				vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
-				if (face.mFlat)
-				{
-					vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
-				}
-				if (face.mFaceID & LL_FACE_INNER_SIDE)
-				{
-					vf.mTypeMask |= LLVolumeFace::INNER_MASK;
-					if (face.mFlat && vf.mNumS > 2)
-					{ //flat inner faces have to copy vert normals
-						vf.mNumS = vf.mNumS*2;
-						if (vf.mNumS < 0)
-						{
-							llerrs << "Volume face corruption detected." << llendl;
-						}
-					}
-				}
-				else
-				{
-					vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
-				}
-			}
-		}
+LLVolumeFace::VertexData::VertexData()
+{
+	mData = NULL;
+	init();
+}
+	
+LLVolumeFace::VertexData::VertexData(const VertexData& rhs)
+{
+	mData = NULL;
+	*this = rhs;
+}
 
-		for (face_list_t::iterator iter = mVolumeFaces.begin();
-			 iter != mVolumeFaces.end(); ++iter)
-		{
-			(*iter).create(this, partial_build);
-		}
+const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs)
+{
+	if (this != &rhs)
+	{
+		init();
+		LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a));
+		mTexCoord = rhs.mTexCoord;
 	}
+	return *this;
 }
 
-
-inline LLVector3 sculpt_rgb_to_vector(U8 r, U8 g, U8 b)
+LLVolumeFace::VertexData::~VertexData()
 {
-	// maps RGB values to vector values [0..255] -> [-0.5..0.5]
-	LLVector3 value;
-	value.mV[VX] = r / 255.f - 0.5f;
-	value.mV[VY] = g / 255.f - 0.5f;
-	value.mV[VZ] = b / 255.f - 0.5f;
-
-	return value;
+	ll_aligned_free_16(mData);
+	mData = NULL;
 }
 
-inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+LLVector4a& LLVolumeFace::VertexData::getPosition()
 {
-	U32 index = (x + y * sculpt_width) * sculpt_components;
-	return index;
+	return mData[POSITION];
 }
 
+LLVector4a& LLVolumeFace::VertexData::getNormal()
+{
+	return mData[NORMAL];
+}
 
-inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+const LLVector4a& LLVolumeFace::VertexData::getPosition() const
 {
-	U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width);
-	U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height);
+	return mData[POSITION];
+}
 
-	return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+const LLVector4a& LLVolumeFace::VertexData::getNormal() const
+{
+	return mData[NORMAL];
 }
 
 
-inline LLVector3 sculpt_index_to_vector(U32 index, const U8* sculpt_data)
+void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos)
 {
-	LLVector3 v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]);
-
-	return v;
+	mData[POSITION] = pos;
 }
 
-inline LLVector3 sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm)
 {
-	U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components);
-
-	return sculpt_index_to_vector(index, sculpt_data);
+	mData[NORMAL] = norm;
 }
 
-inline LLVector3 sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const
 {
-	U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+	const F32* lp = this->getPosition().getF32ptr();
+	const F32* rp = rhs.getPosition().getF32ptr();
 
-	return sculpt_index_to_vector(index, sculpt_data);
-}
+	if (lp[0] != rp[0])
+	{
+		return lp[0] < rp[0];
+	}
 
+	if (rp[1] != lp[1])
+	{
+		return lp[1] < rp[1];
+	}
 
-F32 LLVolume::sculptGetSurfaceArea()
-{
-	// test to see if image has enough variation to create non-degenerate geometry
+	if (rp[2] != lp[2])
+	{
+		return lp[2] < rp[2];
+	}
 
-	F32 area = 0;
+	lp = getNormal().getF32ptr();
+	rp = rhs.getNormal().getF32ptr();
 
-	S32 sizeS = mPathp->mPath.size();
-	S32 sizeT = mProfilep->mProfile.size();
-			
-	for (S32 s = 0; s < sizeS-1; s++)
+	if (lp[0] != rp[0])
 	{
-		for (S32 t = 0; t < sizeT-1; t++)
-		{
-			// get four corners of quad
-			LLVector3 p1 = mMesh[(s  )*sizeT + (t  )].mPos;
-			LLVector3 p2 = mMesh[(s+1)*sizeT + (t  )].mPos;
-			LLVector3 p3 = mMesh[(s  )*sizeT + (t+1)].mPos;
-			LLVector3 p4 = mMesh[(s+1)*sizeT + (t+1)].mPos;
+		return lp[0] < rp[0];
+	}
 
-			// compute the area of the quad by taking the length of the cross product of the two triangles
-			LLVector3 cross1 = (p1 - p2) % (p1 - p3);
-			LLVector3 cross2 = (p4 - p2) % (p4 - p3);
-			area += (cross1.magVec() + cross2.magVec()) / 2.0;
-		}
+	if (rp[1] != lp[1])
+	{
+		return lp[1] < rp[1];
 	}
 
-	return area;
+	if (rp[2] != lp[2])
+	{
+		return lp[2] < rp[2];
+	}
+
+	if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0])
+	{
+		return mTexCoord.mV[0] < rhs.mTexCoord.mV[0];
+	}
+
+	return mTexCoord.mV[1] < rhs.mTexCoord.mV[1];
 }
 
-// create placeholder shape
-void LLVolume::sculptGeneratePlaceholder()
+bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const
 {
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	
-	S32 sizeS = mPathp->mPath.size();
-	S32 sizeT = mProfilep->mProfile.size();
-	
-	S32 line = 0;
+	return mData[POSITION].equals3(rhs.getPosition()) &&
+			mData[NORMAL].equals3(rhs.getNormal()) &&
+			mTexCoord == rhs.mTexCoord;
+}
 
-	// for now, this is a sphere.
-	for (S32 s = 0; s < sizeS; s++)
+bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const
+{
+	bool retval = false;
+	if (rhs.mData[POSITION].equals3(mData[POSITION]) && rhs.mTexCoord == mTexCoord)
 	{
-		for (S32 t = 0; t < sizeT; t++)
+		if (angle_cutoff > 1.f)
 		{
-			S32 i = t + line;
-			Point& pt = mMesh[i];
-
-			
-			F32 u = (F32)s/(sizeS-1);
-			F32 v = (F32)t/(sizeT-1);
-
-			const F32 RADIUS = (F32) 0.3;
-					
-			pt.mPos.mV[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
-			pt.mPos.mV[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
-			pt.mPos.mV[2] = (F32)(cos(F_PI * v) * RADIUS);
-
+			retval = (mData[NORMAL].equals3(rhs.mData[NORMAL]));
+		}
+		else
+		{
+			F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32();
+			retval = cur_angle > angle_cutoff;
 		}
-		line += sizeT;
 	}
+
+	return retval;
 }
 
-// create the vertices from the map
-void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type)
+bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 {
-	U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
-	BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
-	BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
-	BOOL reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror);  // XOR
-	
-	
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	
-	S32 sizeS = mPathp->mPath.size();
-	S32 sizeT = mProfilep->mProfile.size();
+	//input stream is now pointing at a zlib compressed block of LLSD
+	//decompress block
+	LLSD mdl;
+	if (!unzip_llsd(mdl, is, size))
+	{
+		llwarns << "not a valid mesh asset!" << llendl;
+		return false;
+	}
 	
-	S32 line = 0;
-	for (S32 s = 0; s < sizeS; s++)
 	{
-		// Run along the profile.
-		for (S32 t = 0; t < sizeT; t++)
+		U32 face_count = mdl.size();
+
+		if (face_count == 0)
 		{
-			S32 i = t + line;
-			Point& pt = mMesh[i];
+			llerrs << "WTF?" << llendl;
+		}
 
-			S32 reversed_t = t;
+		mVolumeFaces.resize(face_count);
 
-			if (reverse_horizontal)
+		for (U32 i = 0; i < face_count; ++i)
+		{
+			LLSD::Binary pos = mdl[i]["Position"];
+			LLSD::Binary norm = mdl[i]["Normal"];
+			LLSD::Binary tc = mdl[i]["TexCoord0"];
+			LLSD::Binary idx = mdl[i]["TriangleList"];
+
+			LLVolumeFace& face = mVolumeFaces[i];
+
+			//copy out indices
+			face.resizeIndices(idx.size()/2);
+			
+			if (idx.empty() || face.mNumIndices < 3)
+			{ //why is there an empty index list?
+				llerrs <<"WTF?" << llendl;
+				continue;
+			}
+
+			U16* indices = (U16*) &(idx[0]);
+			U32 count = idx.size()/2;
+			for (U32 j = 0; j < count; ++j)
 			{
-				reversed_t = sizeT - t - 1;
+				face.mIndices[j] = indices[j];
 			}
-			
-			U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width);
-			U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
 
-			
-			if (y == 0)  // top row stitching
+			//copy out vertices
+			U32 num_verts = pos.size()/(3*2);
+			face.resizeVertices(num_verts);
+
+			LLVector3 minp;
+			LLVector3 maxp;
+			LLVector2 min_tc; 
+			LLVector2 max_tc; 
+		
+			minp.setValue(mdl[i]["PositionDomain"]["Min"]);
+			maxp.setValue(mdl[i]["PositionDomain"]["Max"]);
+			LLVector4a min_pos, max_pos;
+			min_pos.load3(minp.mV);
+			max_pos.load3(maxp.mV);
+
+			min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
+			max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
+
+			LLVector4a pos_range;
+			pos_range.setSub(max_pos, min_pos);
+			LLVector2 tc_range2 = max_tc - min_tc;
+			LLVector4a tc_range;
+			tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]);
+			LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]);
+
+			LLVector4a* pos_out = face.mPositions;
+			LLVector4a* norm_out = face.mNormals;
+			LLVector4a* tc_out = (LLVector4a*) face.mTexCoords;
+
 			{
-				// pinch?
-				if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+				U16* v = (U16*) &(pos[0]);
+				for (U32 j = 0; j < num_verts; ++j)
 				{
-					x = sculpt_width / 2;
+					pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]);
+					pos_out->div(65535.f);
+					pos_out->mul(pos_range);
+					pos_out->add(min_pos);
+					pos_out++;
+					v += 3;
 				}
+
 			}
 
-			if (y == sculpt_height)  // bottom row stitching
 			{
-				// wrap?
-				if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
+				U16* n = (U16*) &(norm[0]);
+				for (U32 j = 0; j < num_verts; ++j)
 				{
-					y = 0;
+					norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]);
+					norm_out->div(65535.f);
+					norm_out->mul(2.f);
+					norm_out->sub(1.f);
+					norm_out++;
+					n += 3;
 				}
-				else
+			}
+
+			{
+				U16* t = (U16*) &(tc[0]);
+				for (U32 j = 0; j < num_verts; j+=2)
 				{
-					y = sculpt_height - 1;
+					if (j < num_verts-1)
+					{
+						tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]);
+					}
+					else
+					{
+						tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f);
+					}
+
+					t += 4;
+
+					tc_out->div(65535.f);
+					tc_out->mul(tc_range);
+					tc_out->add(min_tc4);
+
+					tc_out++;
 				}
+			}
 
-				// pinch?
-				if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+			if (mdl[i].has("Weights"))
+			{
+				face.allocateWeights(num_verts);
+
+				LLSD::Binary weights = mdl[i]["Weights"];
+
+				U32 idx = 0;
+
+				U32 cur_vertex = 0;
+				while (idx < weights.size() && cur_vertex < num_verts)
 				{
-					x = sculpt_width / 2;
+					const U8 END_INFLUENCES = 0xFF;
+					U8 joint = weights[idx++];
+
+					U32 cur_influence = 0;
+					LLVector4 wght(0,0,0,0);
+
+					while (joint != END_INFLUENCES && idx < weights.size())
+					{
+						U16 influence = weights[idx++];
+						influence |= ((U16) weights[idx++] << 8);
+
+						F32 w = llclamp((F32) influence / 65535.f, 0.f, 0.99999f);
+						wght.mV[cur_influence++] = (F32) joint + w;
+
+						if (cur_influence >= 4)
+						{
+							joint = END_INFLUENCES;
+						}
+						else
+						{
+							joint = weights[idx++];
+						}
+					}
+
+					face.mWeights[cur_vertex].loadua(wght.mV);
+
+					cur_vertex++;
+				}
+
+				if (cur_vertex != num_verts || idx != weights.size())
+				{
+					llwarns << "Vertex weight count does not match vertex count!" << llendl;
 				}
+					
 			}
 
-			if (x == sculpt_width)   // side stitching
+			// modifier flags?
+			bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR);
+			bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT);
+			
+			
+			// translate to actions:
+			bool do_reflect_x = false;
+			bool do_reverse_triangles = false;
+			bool do_invert_normals = false;
+			
+			if (do_mirror)
 			{
-				// wrap?
-				if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
-					(sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
-					(sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
+				do_reflect_x = true;
+				do_reverse_triangles = !do_reverse_triangles;
+			}
+			
+			if (do_invert)
+			{
+				do_invert_normals = true;
+				do_reverse_triangles = !do_reverse_triangles;
+			}
+			
+			// now do the work
+
+			if (do_reflect_x)
+			{
+				LLVector4a* p = (LLVector4a*) face.mPositions;
+				LLVector4a* n = (LLVector4a*) face.mNormals;
+				
+				for (S32 i = 0; i < face.mNumVertices; i++)
 				{
-					x = 0;
+					p[i].mul(-1.0f);
+					n[i].mul(-1.0f);
 				}
-					
-				else
+			}
+
+			if (do_invert_normals)
+			{
+				LLVector4a* n = (LLVector4a*) face.mNormals;
+				
+				for (S32 i = 0; i < face.mNumVertices; i++)
 				{
-					x = sculpt_width - 1;
+					n[i].mul(-1.0f);
 				}
 			}
 
-			pt.mPos = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data);
+			if (do_reverse_triangles)
+			{
+				for (U32 j = 0; j < face.mNumIndices; j += 3)
+				{
+					// swap the 2nd and 3rd index
+					S32 swap = face.mIndices[j+1];
+					face.mIndices[j+1] = face.mIndices[j+2];
+					face.mIndices[j+2] = swap;
+				}
+			}
 
-			if (sculpt_mirror)
+			//calculate bounding box
+			LLVector4a& min = face.mExtents[0];
+			LLVector4a& max = face.mExtents[1];
+
+			min.clear();
+			max.clear();
+			min = max = face.mPositions[0];
+
+			for (S32 i = 1; i < face.mNumVertices; ++i)
 			{
-				pt.mPos.mV[VX] *= -1.f;
+				min.setMin(min, face.mPositions[i]);
+				max.setMax(max, face.mPositions[i]);
 			}
 		}
-		
-		line += sizeT;
 	}
-}
+	
+	mSculptLevel = 0;  // success!
 
+	cacheOptimize();
 
-const S32 SCULPT_REZ_1 = 6;  // changed from 4 to 6 - 6 looks round whereas 4 looks square
-const S32 SCULPT_REZ_2 = 8;
-const S32 SCULPT_REZ_3 = 16;
-const S32 SCULPT_REZ_4 = 32;
+	return true;
+}
 
-S32 sculpt_sides(F32 detail)
+void tetrahedron_set_normal(LLVolumeFace::VertexData* cv)
 {
-
-	// detail is usually one of: 1, 1.5, 2.5, 4.0.
+	LLVector4a v0;
+	v0.setSub(cv[1].getPosition(), cv[0].getNormal());
+	LLVector4a v1;
+	v1.setSub(cv[2].getNormal(), cv[0].getPosition());
 	
-	if (detail <= 1.0)
-	{
-		return SCULPT_REZ_1;
-	}
-	if (detail <= 2.0)
-	{
-		return SCULPT_REZ_2;
-	}
-	if (detail <= 3.0)
-	{
-		return SCULPT_REZ_3;
-	}
-	else
-	{
-		return SCULPT_REZ_4;
-	}
+	cv[0].getNormal().setCross3(v0,v1);
+	cv[0].getNormal().normalize3fast();
+	cv[1].setNormal(cv[0].getNormal());
+	cv[2].setNormal(cv[1].getNormal());
 }
 
+BOOL LLVolume::isTetrahedron()
+{
+	return mIsTetrahedron;
+}
 
-
-// determine the number of vertices in both s and t direction for this sculpt
-void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t)
+void LLVolume::makeTetrahedron()
 {
-	// this code has the following properties:
-	// 1) the aspect ratio of the mesh is as close as possible to the ratio of the map
-	//    while still using all available verts
-	// 2) the mesh cannot have more verts than is allowed by LOD
-	// 3) the mesh cannot have more verts than is allowed by the map
-	
-	S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0);
-	S32 max_vertices_map = width * height / 4;
-	
-	S32 vertices;
-	if (max_vertices_map > 0)
-		vertices = llmin(max_vertices_lod, max_vertices_map);
-	else
-		vertices = max_vertices_lod;
-	
+	mVolumeFaces.clear();
 
-	F32 ratio;
-	if ((width == 0) || (height == 0))
-		ratio = 1.f;
-	else
-		ratio = (F32) width / (F32) height;
+	LLVolumeFace face;
+
+	F32 x = 0.25f;
+	LLVector4a p[] = 
+	{ //unit tetrahedron corners
+		LLVector4a(x,x,x),
+		LLVector4a(-x,-x,x),
+		LLVector4a(-x,x,-x),
+		LLVector4a(x,-x,-x)
+	};
 
+	face.mExtents[0].splat(-x);
+	face.mExtents[1].splat(x);
 	
-	s = (S32)fsqrtf(((F32)vertices / ratio));
+	LLVolumeFace::VertexData cv[3];
 
-	s = llmax(s, 4);              // no degenerate sizes, please
-	t = vertices / s;
+	//set texture coordinates
+	cv[0].mTexCoord = LLVector2(0,0);
+	cv[1].mTexCoord = LLVector2(1,0);
+	cv[2].mTexCoord = LLVector2(0.5f, 0.5f*F_SQRT3);
 
-	t = llmax(t, 4);              // no degenerate sizes, please
-	s = vertices / t;
-}
 
-// sculpt replaces generate() for sculpted surfaces
-void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level)
-{
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-    U8 sculpt_type = mParams.getSculptType();
+	//side 1
+	cv[0].setPosition(p[1]);
+	cv[1].setPosition(p[0]);
+	cv[2].setPosition(p[2]);
 
-	BOOL data_is_empty = FALSE;
+	tetrahedron_set_normal(cv);
 
-	if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
-	{
-		sculpt_level = -1;
-		data_is_empty = TRUE;
-	}
+	face.resizeVertices(12);
+	face.resizeIndices(12);
 
-	S32 requested_sizeS = 0;
-	S32 requested_sizeT = 0;
+	LLVector4a* v = (LLVector4a*) face.mPositions;
+	LLVector4a* n = (LLVector4a*) face.mNormals;
+	LLVector2* tc = (LLVector2*) face.mTexCoords;
 
-	sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT);
+	v[0] = cv[0].getPosition();
+	v[1] = cv[1].getPosition();
+	v[2] = cv[2].getPosition();
+	v += 3;
 
-	mPathp->generate(mParams.getPathParams(), mDetail, 0, TRUE, requested_sizeS);
-	mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, TRUE, requested_sizeT);
+	n[0] = cv[0].getNormal();
+	n[1] = cv[1].getNormal();
+	n[2] = cv[2].getNormal();
+	n += 3;
 
-	S32 sizeS = mPathp->mPath.size();         // we requested a specific size, now see what we really got
-	S32 sizeT = mProfilep->mProfile.size();   // we requested a specific size, now see what we really got
+	tc[0] = cv[0].mTexCoord;
+	tc[1] = cv[1].mTexCoord;
+	tc[2] = cv[2].mTexCoord;
+	tc += 3;
 
-	// weird crash bug - DEV-11158 - trying to collect more data:
-	if ((sizeS == 0) || (sizeT == 0))
-	{
-		llwarns << "sculpt bad mesh size " << sizeS << " " << sizeT << llendl;
-	}
 	
-	sNumMeshPoints -= mMesh.size();
-	mMesh.resize(sizeS * sizeT);
-	sNumMeshPoints += mMesh.size();
-
-	//generate vertex positions
-	if (!data_is_empty)
-	{
-		sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type);
-
-		// don't test lowest LOD to support legacy content DEV-33670
-		if (mDetail > SCULPT_MIN_AREA_DETAIL)
-		{
-			if (sculptGetSurfaceArea() < SCULPT_MIN_AREA)
-			{
-				data_is_empty = TRUE;
-			}
-		}
-	}
-
-	if (data_is_empty)
-	{
-		sculptGeneratePlaceholder();
-	}
-
-
+	//side 2
+	cv[0].setPosition(p[3]);
+	cv[1].setPosition(p[0]);
+	cv[2].setPosition(p[1]);
+
+	tetrahedron_set_normal(cv);
+
+	v[0] = cv[0].getPosition();
+	v[1] = cv[1].getPosition();
+	v[2] = cv[2].getPosition();
+	v += 3;
+
+	n[0] = cv[0].getNormal();
+	n[1] = cv[1].getNormal();
+	n[2] = cv[2].getNormal();
+	n += 3;
+
+	tc[0] = cv[0].mTexCoord;
+	tc[1] = cv[1].mTexCoord;
+	tc[2] = cv[2].mTexCoord;
+	tc += 3;
 	
-	for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
+	//side 3
+	cv[0].setPosition(p[3]);
+	cv[1].setPosition(p[1]);
+	cv[2].setPosition(p[2]);
+
+	tetrahedron_set_normal(cv);
+
+	v[0] = cv[0].getPosition();
+	v[1] = cv[1].getPosition();
+	v[2] = cv[2].getPosition();
+	v += 3;
+
+	n[0] = cv[0].getNormal();
+	n[1] = cv[1].getNormal();
+	n[2] = cv[2].getNormal();
+	n += 3;
+
+	tc[0] = cv[0].mTexCoord;
+	tc[1] = cv[1].mTexCoord;
+	tc[2] = cv[2].mTexCoord;
+	tc += 3;
+	
+	//side 4
+	cv[0].setPosition(p[2]);
+	cv[1].setPosition(p[0]);
+	cv[2].setPosition(p[3]);
+
+	tetrahedron_set_normal(cv);
+
+	v[0] = cv[0].getPosition();
+	v[1] = cv[1].getPosition();
+	v[2] = cv[2].getPosition();
+	v += 3;
+
+	n[0] = cv[0].getNormal();
+	n[1] = cv[1].getNormal();
+	n[2] = cv[2].getNormal();
+	n += 3;
+
+	tc[0] = cv[0].mTexCoord;
+	tc[1] = cv[1].mTexCoord;
+	tc[2] = cv[2].mTexCoord;
+	tc += 3;
+	
+	//set index buffer
+	for (U16 i = 0; i < 12; i++)
 	{
-		mFaceMask |= mProfilep->mFaces[i].mFaceID;
+		face.mIndices[i] = i;
 	}
-
-	mSculptLevel = sculpt_level;
-
-	// Delete any existing faces so that they get regenerated
-	mVolumeFaces.clear();
 	
-	createVolumeFaces();
+	mVolumeFaces.push_back(face);
+	mSculptLevel = 0;
+	mIsTetrahedron = TRUE;
 }
 
-
-
-
-BOOL LLVolume::isCap(S32 face)
+void LLVolume::copyVolumeFaces(const LLVolume* volume)
 {
-	return mProfilep->mFaces[face].mCap; 
+	mVolumeFaces = volume->mVolumeFaces;
+	mSculptLevel = 0;
+	mIsTetrahedron = FALSE;
 }
 
-BOOL LLVolume::isFlat(S32 face)
+void LLVolume::cacheOptimize()
 {
-	return mProfilep->mFaces[face].mFlat;
+	for (S32 i = 0; i < mVolumeFaces.size(); ++i)
+	{
+		mVolumeFaces[i].cacheOptimize();
+	}
 }
 
 
-bool LLVolumeParams::operator==(const LLVolumeParams &params) const
+S32	LLVolume::getNumFaces() const
 {
-	return ( (getPathParams() == params.getPathParams()) &&
-			 (getProfileParams() == params.getProfileParams()) &&
-			 (mSculptID == params.mSculptID) &&
-			 (mSculptType == params.mSculptType) );
-}
+	U8 sculpt_type = (mParams.getSculptType() & LL_SCULPT_TYPE_MASK);
 
-bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
-{
-	return ( (getPathParams() != params.getPathParams()) ||
-			 (getProfileParams() != params.getProfileParams()) ||
-			 (mSculptID != params.mSculptID) ||
-			 (mSculptType != params.mSculptType) );
+	if (sculpt_type == LL_SCULPT_TYPE_MESH)
+	{
+		return LL_SCULPT_MESH_MAX_FACES;
+	}
+
+	return (S32)mProfilep->mFaces.size();
 }
 
-bool LLVolumeParams::operator<(const LLVolumeParams &params) const
+
+void LLVolume::createVolumeFaces()
 {
-	if( getPathParams() != params.getPathParams() )
-	{
-		return getPathParams() < params.getPathParams();
-	}
-	
-	if (getProfileParams() != params.getProfileParams())
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+	if (mGenerateSingleFace)
 	{
-		return getProfileParams() < params.getProfileParams();
+		// do nothing
 	}
-	
-	if (mSculptID != params.mSculptID)
+	else
 	{
-		return mSculptID < params.mSculptID;
-	}
-
+		S32 num_faces = getNumFaces();
+		BOOL partial_build = TRUE;
+		if (num_faces != mVolumeFaces.size())
+		{
+			partial_build = FALSE;
+			mVolumeFaces.resize(num_faces);
+		}
+		// Initialize volume faces with parameter data
+		for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++)
+		{
+			LLVolumeFace& vf = mVolumeFaces[i];
+			LLProfile::Face& face = mProfilep->mFaces[i];
+			vf.mBeginS = face.mIndex;
+			vf.mNumS = face.mCount;
+			if (vf.mNumS < 0)
+			{
+				llerrs << "Volume face corruption detected." << llendl;
+			}
 
-	return mSculptType < params.mSculptType;
+			vf.mBeginT = 0;
+			vf.mNumT= getPath().mPath.size();
+			vf.mID = i;
 
+			// Set the type mask bits correctly
+			if (mParams.getProfileParams().getHollow() > 0)
+			{
+				vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
+			}
+			if (mProfilep->isOpen())
+			{
+				vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
+			}
+			if (face.mCap)
+			{
+				vf.mTypeMask |= LLVolumeFace::CAP_MASK;
+				if (face.mFaceID == LL_FACE_PATH_BEGIN)
+				{
+					vf.mTypeMask |= LLVolumeFace::TOP_MASK;
+				}
+				else
+				{
+					llassert(face.mFaceID == LL_FACE_PATH_END);
+					vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
+				}
+			}
+			else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
+			{
+				vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
+			}
+			else
+			{
+				vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
+				if (face.mFlat)
+				{
+					vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
+				}
+				if (face.mFaceID & LL_FACE_INNER_SIDE)
+				{
+					vf.mTypeMask |= LLVolumeFace::INNER_MASK;
+					if (face.mFlat && vf.mNumS > 2)
+					{ //flat inner faces have to copy vert normals
+						vf.mNumS = vf.mNumS*2;
+						if (vf.mNumS < 0)
+						{
+							llerrs << "Volume face corruption detected." << llendl;
+						}
+					}
+				}
+				else
+				{
+					vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
+				}
+			}
+		}
 
+		for (face_list_t::iterator iter = mVolumeFaces.begin();
+			 iter != mVolumeFaces.end(); ++iter)
+		{
+			(*iter).create(this, partial_build);
+		}
+	}
 }
 
-void LLVolumeParams::copyParams(const LLVolumeParams &params)
-{
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	mProfileParams.copyParams(params.mProfileParams);
-	mPathParams.copyParams(params.mPathParams);
-	mSculptID = params.getSculptID();
-	mSculptType = params.getSculptType();
-}
 
-// Less restricitve approx 0 for volumes
-const F32 APPROXIMATELY_ZERO = 0.001f;
-bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
+inline LLVector3 sculpt_rgb_to_vector(U8 r, U8 g, U8 b)
 {
-	return (f >= -tolerance) && (f <= tolerance);
+	// maps RGB values to vector values [0..255] -> [-0.5..0.5]
+	LLVector3 value;
+	value.mV[VX] = r / 255.f - 0.5f;
+	value.mV[VY] = g / 255.f - 0.5f;
+	value.mV[VZ] = b / 255.f - 0.5f;
+
+	return value;
 }
 
-// return true if in range (or nearly so)
-static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
+inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
 {
-	F32 min_delta = v - min;
-	if (min_delta < 0.f)
-	{
-		v = min;
-		if (!approx_zero(min_delta, tolerance))
-			return false;
-	}
-	F32 max_delta = max - v;
-	if (max_delta < 0.f)
-	{
-		v = max;
-		if (!approx_zero(max_delta, tolerance))
-			return false;
-	}
-	return true;
+	U32 index = (x + y * sculpt_width) * sculpt_components;
+	return index;
 }
 
-bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
-{
-	bool valid = true;
 
-	// First, clamp to valid ranges.
-	F32 begin = b;
-	valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+{
+	U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width);
+	U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height);
 
-	F32 end = e;
-	if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
-	valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+	return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+}
 
-	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
 
-	// Now set them.
-	mProfileParams.setBegin(begin);
-	mProfileParams.setEnd(end);
+inline LLVector3 sculpt_index_to_vector(U32 index, const U8* sculpt_data)
+{
+	LLVector3 v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]);
 
-	return valid;
+	return v;
 }
 
-bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
+inline LLVector3 sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
 {
-	bool valid = true;
-
-	// First, clamp to valid ranges.
-	F32 begin = b;
-	valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+	U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components);
 
-	F32 end = e;
-	valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+	return sculpt_index_to_vector(index, sculpt_data);
+}
 
-	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+inline LLVector3 sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+{
+	U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
 
-	// Now set them.
-	mPathParams.setBegin(begin);
-	mPathParams.setEnd(end);
+	return sculpt_index_to_vector(index, sculpt_data);
+}
 
-	return valid;
-}			
 
-bool LLVolumeParams::setHollow(const F32 h)
+F32 LLVolume::sculptGetSurfaceArea()
 {
-	// Validate the hollow based on path and profile.
-	U8 profile 	= mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
-	U8 hole_type 	= mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
-	
-	F32 max_hollow = HOLLOW_MAX;
+	// test to see if image has enough variation to create non-degenerate geometry
 
-	// Only square holes have trouble.
-	if (LL_PCODE_HOLE_SQUARE == hole_type)
+	F32 area = 0;
+
+	S32 sizeS = mPathp->mPath.size();
+	S32 sizeT = mProfilep->mProfile.size();
+			
+	for (S32 s = 0; s < sizeS-1; s++)
 	{
-		switch(profile)
+		for (S32 t = 0; t < sizeT-1; t++)
 		{
-		case LL_PCODE_PROFILE_CIRCLE:
-		case LL_PCODE_PROFILE_CIRCLE_HALF:
-		case LL_PCODE_PROFILE_EQUALTRI:
-			max_hollow = HOLLOW_MAX_SQUARE;
+			// get four corners of quad
+			LLVector3 p1 = mMesh[(s  )*sizeT + (t  )].mPos;
+			LLVector3 p2 = mMesh[(s+1)*sizeT + (t  )].mPos;
+			LLVector3 p3 = mMesh[(s  )*sizeT + (t+1)].mPos;
+			LLVector3 p4 = mMesh[(s+1)*sizeT + (t+1)].mPos;
+
+			// compute the area of the quad by taking the length of the cross product of the two triangles
+			LLVector3 cross1 = (p1 - p2) % (p1 - p3);
+			LLVector3 cross2 = (p4 - p2) % (p4 - p3);
+			area += (cross1.magVec() + cross2.magVec()) / 2.0;
 		}
 	}
 
-	F32 hollow = h;
-	bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
-	mProfileParams.setHollow(hollow); 
-
-	return valid;
-}	
+	return area;
+}
 
-bool LLVolumeParams::setTwistBegin(const F32 b)
+// create placeholder shape
+void LLVolume::sculptGeneratePlaceholder()
 {
-	F32 twist_begin = b;
-	bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
-	mPathParams.setTwistBegin(twist_begin);
-	return valid;
-}
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	S32 sizeS = mPathp->mPath.size();
+	S32 sizeT = mProfilep->mProfile.size();
+	
+	S32 line = 0;
 
-bool LLVolumeParams::setTwistEnd(const F32 e)
-{	
-	F32 twist_end = e;
-	bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
-	mPathParams.setTwistEnd(twist_end);
-	return valid;
+	// for now, this is a sphere.
+	for (S32 s = 0; s < sizeS; s++)
+	{
+		for (S32 t = 0; t < sizeT; t++)
+		{
+			S32 i = t + line;
+			Point& pt = mMesh[i];
+
+			
+			F32 u = (F32)s/(sizeS-1);
+			F32 v = (F32)t/(sizeT-1);
+
+			const F32 RADIUS = (F32) 0.3;
+					
+			pt.mPos.mV[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
+			pt.mPos.mV[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
+			pt.mPos.mV[2] = (F32)(cos(F_PI * v) * RADIUS);
+
+		}
+		line += sizeT;
+	}
 }
 
-bool LLVolumeParams::setRatio(const F32 x, const F32 y)
+// create the vertices from the map
+void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type)
 {
-	F32 min_x = RATIO_MIN;
-	F32 max_x = RATIO_MAX;
-	F32 min_y = RATIO_MIN;
-	F32 max_y = RATIO_MAX;
-	// If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
-	U8 path_type 	= mPathParams.getCurveType();
-	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
-	if ( LL_PCODE_PATH_CIRCLE == path_type &&
-		 LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
+	U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+	BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
+	BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+	BOOL reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror);  // XOR
+	
+	
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	S32 sizeS = mPathp->mPath.size();
+	S32 sizeT = mProfilep->mProfile.size();
+	
+	S32 line = 0;
+	for (S32 s = 0; s < sizeS; s++)
 	{
-		// Holes are more restricted...
-		min_x = HOLE_X_MIN;
-		max_x = HOLE_X_MAX;
-		min_y = HOLE_Y_MIN;
-		max_y = HOLE_Y_MAX;
-	}
+		// Run along the profile.
+		for (S32 t = 0; t < sizeT; t++)
+		{
+			S32 i = t + line;
+			Point& pt = mMesh[i];
 
-	F32 ratio_x = x;
-	bool valid = limit_range(ratio_x, min_x, max_x);
-	F32 ratio_y = y;
-	valid &= limit_range(ratio_y, min_y, max_y);
+			S32 reversed_t = t;
 
-	mPathParams.setScale(ratio_x, ratio_y);
+			if (reverse_horizontal)
+			{
+				reversed_t = sizeT - t - 1;
+			}
+			
+			U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width);
+			U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
 
-	return valid;
-}
+			
+			if (y == 0)  // top row stitching
+			{
+				// pinch?
+				if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+				{
+					x = sculpt_width / 2;
+				}
+			}
 
-bool LLVolumeParams::setShear(const F32 x, const F32 y)
-{
-	F32 shear_x = x;
-	bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
-	F32 shear_y = y;
-	valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
-	mPathParams.setShear(shear_x, shear_y);
-	return valid;
-}
+			if (y == sculpt_height)  // bottom row stitching
+			{
+				// wrap?
+				if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
+				{
+					y = 0;
+				}
+				else
+				{
+					y = sculpt_height - 1;
+				}
 
-bool LLVolumeParams::setTaperX(const F32 v)
-{
-	F32 taper = v;
-	bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
-	mPathParams.setTaperX(taper);
-	return valid;
-}
+				// pinch?
+				if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+				{
+					x = sculpt_width / 2;
+				}
+			}
 
-bool LLVolumeParams::setTaperY(const F32 v)
-{
-	F32 taper = v;
-	bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
-	mPathParams.setTaperY(taper);
-	return valid;
-}
+			if (x == sculpt_width)   // side stitching
+			{
+				// wrap?
+				if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
+					(sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
+					(sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
+				{
+					x = 0;
+				}
+					
+				else
+				{
+					x = sculpt_width - 1;
+				}
+			}
 
-bool LLVolumeParams::setRevolutions(const F32 r)
-{
-	F32 revolutions = r;
-	bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
-	mPathParams.setRevolutions(revolutions);
-	return valid;
+			pt.mPos = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data);
+
+			if (sculpt_mirror)
+			{
+				pt.mPos.mV[VX] *= -1.f;
+			}
+		}
+		
+		line += sizeT;
+	}
 }
 
-bool LLVolumeParams::setRadiusOffset(const F32 offset)
+
+const S32 SCULPT_REZ_1 = 6;  // changed from 4 to 6 - 6 looks round whereas 4 looks square
+const S32 SCULPT_REZ_2 = 8;
+const S32 SCULPT_REZ_3 = 16;
+const S32 SCULPT_REZ_4 = 32;
+
+S32 sculpt_sides(F32 detail)
 {
-	bool valid = true;
 
-	// If this is a sphere, just set it to 0 and get out.
-	U8 path_type 	= mPathParams.getCurveType();
-	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
-	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
-		LL_PCODE_PATH_CIRCLE != path_type )
+	// detail is usually one of: 1, 1.5, 2.5, 4.0.
+	
+	if (detail <= 1.0)
 	{
-		mPathParams.setRadiusOffset(0.f);
-		return true;
+		return SCULPT_REZ_1;
 	}
-
-	// Limit radius offset, based on taper and hole size y.
-	F32 radius_offset	= offset;
-	F32 taper_y    		= getTaperY();
-	F32 radius_mag		= fabs(radius_offset);
-	F32 hole_y_mag 		= fabs(getRatioY());
-	F32 taper_y_mag		= fabs(taper_y);
-	// Check to see if the taper effects us.
-	if ( (radius_offset > 0.f && taper_y < 0.f) ||
-			(radius_offset < 0.f && taper_y > 0.f) )
+	if (detail <= 2.0)
 	{
-		// The taper does not help increase the radius offset range.
-		taper_y_mag = 0.f;
+		return SCULPT_REZ_2;
 	}
-	F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
-
-	// Enforce the maximum magnitude.
-	F32 delta = max_radius_mag - radius_mag;
-	if (delta < 0.f)
+	if (detail <= 3.0)
 	{
-		// Check radius offset sign.
-		if (radius_offset < 0.f)
-		{
-			radius_offset = -max_radius_mag;
-		}
-		else
-		{
-			radius_offset = max_radius_mag;
-		}
-		valid = approx_zero(delta, .1f);
+		return SCULPT_REZ_3;
+	}
+	else
+	{
+		return SCULPT_REZ_4;
 	}
-
-	mPathParams.setRadiusOffset(radius_offset);
-	return valid;
 }
 
-bool LLVolumeParams::setSkew(const F32 skew_value)
+
+
+// determine the number of vertices in both s and t direction for this sculpt
+void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t)
 {
-	bool valid = true;
+	// this code has the following properties:
+	// 1) the aspect ratio of the mesh is as close as possible to the ratio of the map
+	//    while still using all available verts
+	// 2) the mesh cannot have more verts than is allowed by LOD
+	// 3) the mesh cannot have more verts than is allowed by the map
+	
+	S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0);
+	S32 max_vertices_map = width * height / 4;
+	
+	S32 vertices;
+	if (max_vertices_map > 0)
+		vertices = llmin(max_vertices_lod, max_vertices_map);
+	else
+		vertices = max_vertices_lod;
+	
 
-	// Check the skew value against the revolutions.
-	F32 skew		= llclamp(skew_value, SKEW_MIN, SKEW_MAX);
-	F32 skew_mag	= fabs(skew);
-	F32 revolutions = getRevolutions();
-	F32 scale_x		= getRatioX();
-	F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
-	// Discontinuity; A revolution of 1 allows skews below 0.5.
-	if ( fabs(revolutions - 1.0f) < 0.001)
-		min_skew_mag = 0.0f;
+	F32 ratio;
+	if ((width == 0) || (height == 0))
+		ratio = 1.f;
+	else
+		ratio = (F32) width / (F32) height;
 
-	// Clip skew.
-	F32 delta = skew_mag - min_skew_mag;
-	if (delta < 0.f)
-	{
-		// Check skew sign.
-		if (skew < 0.0f)
-		{
-			skew = -min_skew_mag;
-		}
-		else 
-		{
-			skew = min_skew_mag;
-		}
-		valid = approx_zero(delta, .01f);
-	}
+	
+	s = (S32)(F32) sqrt(((F32)vertices / ratio));
 
-	mPathParams.setSkew(skew);
-	return valid;
-}
+	s = llmax(s, 4);              // no degenerate sizes, please
+	t = vertices / s;
 
-bool LLVolumeParams::setSculptID(const LLUUID sculpt_id, U8 sculpt_type)
-{
-	mSculptID = sculpt_id;
-	mSculptType = sculpt_type;
-	return true;
+	t = llmax(t, 4);              // no degenerate sizes, please
+	s = vertices / t;
 }
 
-bool LLVolumeParams::setType(U8 profile, U8 path)
+// sculpt replaces generate() for sculpted surfaces
+void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level)
 {
-	bool result = true;
-	// First, check profile and path for validity.
-	U8 profile_type	= profile & LL_PCODE_PROFILE_MASK;
-	U8 hole_type 	= (profile & LL_PCODE_HOLE_MASK) >> 4;
-	U8 path_type	= path >> 4;
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+    U8 sculpt_type = mParams.getSculptType();
 
-	if (profile_type > LL_PCODE_PROFILE_MAX)
-	{
-		// Bad profile.  Make it square.
-		profile = LL_PCODE_PROFILE_SQUARE;
-		result = false;
-		llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
-			 	<< ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
-	}
-	else if (hole_type > LL_PCODE_HOLE_MAX)
-	{
-		// Bad hole.  Make it the same.
-		profile = profile_type;
-		result = false;
-		llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
-			 	<< ") to be LL_PCODE_HOLE_SAME" << llendl;
-	}
+	BOOL data_is_empty = FALSE;
 
-	if (path_type < LL_PCODE_PATH_MIN ||
-		path_type > LL_PCODE_PATH_MAX)
+	if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
 	{
-		// Bad path.  Make it linear.
-		result = false;
-		llwarns << "LLVolumeParams::setType changing bad path (" << path
-			 	<< ") to be LL_PCODE_PATH_LINE" << llendl;
-		path = LL_PCODE_PATH_LINE;
+		sculpt_level = -1;
+		data_is_empty = TRUE;
 	}
 
-	mProfileParams.setCurveType(profile);
-	mPathParams.setCurveType(path);
-	return result;
-}
+	S32 requested_sizeS = 0;
+	S32 requested_sizeT = 0;
 
-// static 
-bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
-		U8 path_curve, F32 path_begin, F32 path_end,
-		F32 scx, F32 scy, F32 shx, F32 shy,
-		F32 twistend, F32 twistbegin, F32 radiusoffset,
-		F32 tx, F32 ty, F32 revolutions, F32 skew)
-{
-	LLVolumeParams test_params;
-	if (!test_params.setType		(prof_curve, path_curve))
-	{
-	    	return false;
-	}
-	if (!test_params.setBeginAndEndS	(prof_begin, prof_end))
-	{
-	    	return false;
-	}
-	if (!test_params.setBeginAndEndT	(path_begin, path_end))
-	{
-	    	return false;
-	}
-	if (!test_params.setHollow		(hollow))
-	{
-	    	return false;
-	}
-	if (!test_params.setTwistBegin		(twistbegin))
-	{
-	    	return false;
-	}
-	if (!test_params.setTwistEnd		(twistend))
+	sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT);
+
+	mPathp->generate(mParams.getPathParams(), mDetail, 0, TRUE, requested_sizeS);
+	mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, TRUE, requested_sizeT);
+
+	S32 sizeS = mPathp->mPath.size();         // we requested a specific size, now see what we really got
+	S32 sizeT = mProfilep->mProfile.size();   // we requested a specific size, now see what we really got
+
+	// weird crash bug - DEV-11158 - trying to collect more data:
+	if ((sizeS == 0) || (sizeT == 0))
 	{
-	    	return false;
+		llwarns << "sculpt bad mesh size " << sizeS << " " << sizeT << llendl;
 	}
-	if (!test_params.setRatio		(scx, scy))
+	
+	sNumMeshPoints -= mMesh.size();
+	mMesh.resize(sizeS * sizeT);
+	sNumMeshPoints += mMesh.size();
+
+	//generate vertex positions
+	if (!data_is_empty)
 	{
-	    	return false;
+		sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type);
+
+		// don't test lowest LOD to support legacy content DEV-33670
+		if (mDetail > SCULPT_MIN_AREA_DETAIL)
+		{
+			if (sculptGetSurfaceArea() < SCULPT_MIN_AREA)
+			{
+				data_is_empty = TRUE;
+			}
+		}
 	}
-	if (!test_params.setShear		(shx, shy))
+
+	if (data_is_empty)
 	{
-	    	return false;
+		sculptGeneratePlaceholder();
 	}
-	if (!test_params.setTaper		(tx, ty))
+
+
+	
+	for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
 	{
-	    	return false;
+		mFaceMask |= mProfilep->mFaces[i].mFaceID;
 	}
-	if (!test_params.setRevolutions		(revolutions))
+
+	mSculptLevel = sculpt_level;
+
+	// Delete any existing faces so that they get regenerated
+	mVolumeFaces.clear();
+	
+	createVolumeFaces();
+}
+
+
+
+
+BOOL LLVolume::isCap(S32 face)
+{
+	return mProfilep->mFaces[face].mCap; 
+}
+
+BOOL LLVolume::isFlat(S32 face)
+{
+	return mProfilep->mFaces[face].mFlat;
+}
+
+
+bool LLVolumeParams::isSculpt() const
+{
+	return mSculptID.notNull();
+}
+
+bool LLVolumeParams::isMeshSculpt() const
+{
+	return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH);
+}
+
+bool LLVolumeParams::operator==(const LLVolumeParams &params) const
+{
+	return ( (getPathParams() == params.getPathParams()) &&
+			 (getProfileParams() == params.getProfileParams()) &&
+			 (mSculptID == params.mSculptID) &&
+			 (mSculptType == params.mSculptType) );
+}
+
+bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
+{
+	return ( (getPathParams() != params.getPathParams()) ||
+			 (getProfileParams() != params.getProfileParams()) ||
+			 (mSculptID != params.mSculptID) ||
+			 (mSculptType != params.mSculptType) );
+}
+
+bool LLVolumeParams::operator<(const LLVolumeParams &params) const
+{
+	if( getPathParams() != params.getPathParams() )
 	{
-	    	return false;
+		return getPathParams() < params.getPathParams();
 	}
-	if (!test_params.setRadiusOffset	(radiusoffset))
+	
+	if (getProfileParams() != params.getProfileParams())
 	{
-	    	return false;
+		return getProfileParams() < params.getProfileParams();
 	}
-	if (!test_params.setSkew		(skew))
+	
+	if (mSculptID != params.mSculptID)
 	{
-	    	return false;
+		return mSculptID < params.mSculptID;
 	}
-	return true;
+
+	return mSculptType < params.mSculptType;
+
+
 }
 
-S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
+void LLVolumeParams::copyParams(const LLVolumeParams &params)
 {
 	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	
-	S32 expected_num_triangle_indices = getNumTriangleIndices();
-	if (expected_num_triangle_indices > MAX_VOLUME_TRIANGLE_INDICES)
+	mProfileParams.copyParams(params.mProfileParams);
+	mPathParams.copyParams(params.mPathParams);
+	mSculptID = params.getSculptID();
+	mSculptType = params.getSculptType();
+}
+
+// Less restricitve approx 0 for volumes
+const F32 APPROXIMATELY_ZERO = 0.001f;
+bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
+{
+	return (f >= -tolerance) && (f <= tolerance);
+}
+
+// return true if in range (or nearly so)
+static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
+{
+	F32 min_delta = v - min;
+	if (min_delta < 0.f)
+	{
+		v = min;
+		if (!approx_zero(min_delta, tolerance))
+			return false;
+	}
+	F32 max_delta = max - v;
+	if (max_delta < 0.f)
+	{
+		v = max;
+		if (!approx_zero(max_delta, tolerance))
+			return false;
+	}
+	return true;
+}
+
+bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
+{
+	bool valid = true;
+
+	// First, clamp to valid ranges.
+	F32 begin = b;
+	valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+	F32 end = e;
+	if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
+	valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+
+	// Now set them.
+	mProfileParams.setBegin(begin);
+	mProfileParams.setEnd(end);
+
+	return valid;
+}
+
+bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
+{
+	bool valid = true;
+
+	// First, clamp to valid ranges.
+	F32 begin = b;
+	valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+	F32 end = e;
+	valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+
+	// Now set them.
+	mPathParams.setBegin(begin);
+	mPathParams.setEnd(end);
+
+	return valid;
+}			
+
+bool LLVolumeParams::setHollow(const F32 h)
+{
+	// Validate the hollow based on path and profile.
+	U8 profile 	= mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+	U8 hole_type 	= mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
+	
+	F32 max_hollow = HOLLOW_MAX;
+
+	// Only square holes have trouble.
+	if (LL_PCODE_HOLE_SQUARE == hole_type)
+	{
+		switch(profile)
+		{
+		case LL_PCODE_PROFILE_CIRCLE:
+		case LL_PCODE_PROFILE_CIRCLE_HALF:
+		case LL_PCODE_PROFILE_EQUALTRI:
+			max_hollow = HOLLOW_MAX_SQUARE;
+		}
+	}
+
+	F32 hollow = h;
+	bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
+	mProfileParams.setHollow(hollow); 
+
+	return valid;
+}	
+
+bool LLVolumeParams::setTwistBegin(const F32 b)
+{
+	F32 twist_begin = b;
+	bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
+	mPathParams.setTwistBegin(twist_begin);
+	return valid;
+}
+
+bool LLVolumeParams::setTwistEnd(const F32 e)
+{	
+	F32 twist_end = e;
+	bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
+	mPathParams.setTwistEnd(twist_end);
+	return valid;
+}
+
+bool LLVolumeParams::setRatio(const F32 x, const F32 y)
+{
+	F32 min_x = RATIO_MIN;
+	F32 max_x = RATIO_MAX;
+	F32 min_y = RATIO_MIN;
+	F32 max_y = RATIO_MAX;
+	// If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
+	U8 path_type 	= mPathParams.getCurveType();
+	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+	if ( LL_PCODE_PATH_CIRCLE == path_type &&
+		 LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
+	{
+		// Holes are more restricted...
+		min_x = HOLE_X_MIN;
+		max_x = HOLE_X_MAX;
+		min_y = HOLE_Y_MIN;
+		max_y = HOLE_Y_MAX;
+	}
+
+	F32 ratio_x = x;
+	bool valid = limit_range(ratio_x, min_x, max_x);
+	F32 ratio_y = y;
+	valid &= limit_range(ratio_y, min_y, max_y);
+
+	mPathParams.setScale(ratio_x, ratio_y);
+
+	return valid;
+}
+
+bool LLVolumeParams::setShear(const F32 x, const F32 y)
+{
+	F32 shear_x = x;
+	bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
+	F32 shear_y = y;
+	valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
+	mPathParams.setShear(shear_x, shear_y);
+	return valid;
+}
+
+bool LLVolumeParams::setTaperX(const F32 v)
+{
+	F32 taper = v;
+	bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+	mPathParams.setTaperX(taper);
+	return valid;
+}
+
+bool LLVolumeParams::setTaperY(const F32 v)
+{
+	F32 taper = v;
+	bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+	mPathParams.setTaperY(taper);
+	return valid;
+}
+
+bool LLVolumeParams::setRevolutions(const F32 r)
+{
+	F32 revolutions = r;
+	bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
+	mPathParams.setRevolutions(revolutions);
+	return valid;
+}
+
+bool LLVolumeParams::setRadiusOffset(const F32 offset)
+{
+	bool valid = true;
+
+	// If this is a sphere, just set it to 0 and get out.
+	U8 path_type 	= mPathParams.getCurveType();
+	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
+		LL_PCODE_PATH_CIRCLE != path_type )
+	{
+		mPathParams.setRadiusOffset(0.f);
+		return true;
+	}
+
+	// Limit radius offset, based on taper and hole size y.
+	F32 radius_offset	= offset;
+	F32 taper_y    		= getTaperY();
+	F32 radius_mag		= fabs(radius_offset);
+	F32 hole_y_mag 		= fabs(getRatioY());
+	F32 taper_y_mag		= fabs(taper_y);
+	// Check to see if the taper effects us.
+	if ( (radius_offset > 0.f && taper_y < 0.f) ||
+			(radius_offset < 0.f && taper_y > 0.f) )
+	{
+		// The taper does not help increase the radius offset range.
+		taper_y_mag = 0.f;
+	}
+	F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+
+	// Enforce the maximum magnitude.
+	F32 delta = max_radius_mag - radius_mag;
+	if (delta < 0.f)
+	{
+		// Check radius offset sign.
+		if (radius_offset < 0.f)
+		{
+			radius_offset = -max_radius_mag;
+		}
+		else
+		{
+			radius_offset = max_radius_mag;
+		}
+		valid = approx_zero(delta, .1f);
+	}
+
+	mPathParams.setRadiusOffset(radius_offset);
+	return valid;
+}
+
+bool LLVolumeParams::setSkew(const F32 skew_value)
+{
+	bool valid = true;
+
+	// Check the skew value against the revolutions.
+	F32 skew		= llclamp(skew_value, SKEW_MIN, SKEW_MAX);
+	F32 skew_mag	= fabs(skew);
+	F32 revolutions = getRevolutions();
+	F32 scale_x		= getRatioX();
+	F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+	// Discontinuity; A revolution of 1 allows skews below 0.5.
+	if ( fabs(revolutions - 1.0f) < 0.001)
+		min_skew_mag = 0.0f;
+
+	// Clip skew.
+	F32 delta = skew_mag - min_skew_mag;
+	if (delta < 0.f)
+	{
+		// Check skew sign.
+		if (skew < 0.0f)
+		{
+			skew = -min_skew_mag;
+		}
+		else 
+		{
+			skew = min_skew_mag;
+		}
+		valid = approx_zero(delta, .01f);
+	}
+
+	mPathParams.setSkew(skew);
+	return valid;
+}
+
+bool LLVolumeParams::setSculptID(const LLUUID sculpt_id, U8 sculpt_type)
+{
+	mSculptID = sculpt_id;
+	mSculptType = sculpt_type;
+	return true;
+}
+
+bool LLVolumeParams::setType(U8 profile, U8 path)
+{
+	bool result = true;
+	// First, check profile and path for validity.
+	U8 profile_type	= profile & LL_PCODE_PROFILE_MASK;
+	U8 hole_type 	= (profile & LL_PCODE_HOLE_MASK) >> 4;
+	U8 path_type	= path >> 4;
+
+	if (profile_type > LL_PCODE_PROFILE_MAX)
+	{
+		// Bad profile.  Make it square.
+		profile = LL_PCODE_PROFILE_SQUARE;
+		result = false;
+		llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
+			 	<< ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
+	}
+	else if (hole_type > LL_PCODE_HOLE_MAX)
+	{
+		// Bad hole.  Make it the same.
+		profile = profile_type;
+		result = false;
+		llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
+			 	<< ") to be LL_PCODE_HOLE_SAME" << llendl;
+	}
+
+	if (path_type < LL_PCODE_PATH_MIN ||
+		path_type > LL_PCODE_PATH_MAX)
+	{
+		// Bad path.  Make it linear.
+		result = false;
+		llwarns << "LLVolumeParams::setType changing bad path (" << path
+			 	<< ") to be LL_PCODE_PATH_LINE" << llendl;
+		path = LL_PCODE_PATH_LINE;
+	}
+
+	mProfileParams.setCurveType(profile);
+	mPathParams.setCurveType(path);
+	return result;
+}
+
+// static 
+bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
+		U8 path_curve, F32 path_begin, F32 path_end,
+		F32 scx, F32 scy, F32 shx, F32 shy,
+		F32 twistend, F32 twistbegin, F32 radiusoffset,
+		F32 tx, F32 ty, F32 revolutions, F32 skew)
+{
+	LLVolumeParams test_params;
+	if (!test_params.setType		(prof_curve, path_curve))
+	{
+	    	return false;
+	}
+	if (!test_params.setBeginAndEndS	(prof_begin, prof_end))
+	{
+	    	return false;
+	}
+	if (!test_params.setBeginAndEndT	(path_begin, path_end))
+	{
+	    	return false;
+	}
+	if (!test_params.setHollow		(hollow))
+	{
+	    	return false;
+	}
+	if (!test_params.setTwistBegin		(twistbegin))
+	{
+	    	return false;
+	}
+	if (!test_params.setTwistEnd		(twistend))
+	{
+	    	return false;
+	}
+	if (!test_params.setRatio		(scx, scy))
+	{
+	    	return false;
+	}
+	if (!test_params.setShear		(shx, shy))
+	{
+	    	return false;
+	}
+	if (!test_params.setTaper		(tx, ty))
+	{
+	    	return false;
+	}
+	if (!test_params.setRevolutions		(revolutions))
+	{
+	    	return false;
+	}
+	if (!test_params.setRadiusOffset	(radiusoffset))
+	{
+	    	return false;
+	}
+	if (!test_params.setSkew		(skew))
+	{
+	    	return false;
+	}
+	return true;
+}
+
+S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
+{
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	S32 expected_num_triangle_indices = getNumTriangleIndices();
+	if (expected_num_triangle_indices > MAX_VOLUME_TRIANGLE_INDICES)
 	{
 		// we don't allow LLVolumes with this many vertices
 		llwarns << "Couldn't allocate triangle indices" << llendl;
@@ -2954,68 +3703,306 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 					}
 				}
 			}
-		}
-		else
-		{
-			// Open solid
+		}
+		else
+		{
+			// Open solid
+
+			for (t = 0; t < size_t - 1; t++)
+			{
+				// Outer face + 1 cut face
+				for (s = 0; s < size_s - 1; s++)
+				{
+					i  = s + t*size_s;
+
+					index[count++]  = i;				// x,y
+					index[count++]  = i + 1;			// x+1,y
+					index[count++]  = i + size_s;		// x,y+1
+
+					index[count++]  = i + size_s;		// x,y+1
+					index[count++]  = i + 1;			// x+1,y
+					index[count++]  = i + size_s + 1;	// x+1,y+1
+				}
+
+				// The other cut face
+				index[count++] = (size_s - 1) + (t*size_s);		// x,y
+				index[count++] = 0 + t*size_s;					// x+1,y
+				index[count++] = (size_s - 1) + (t+1)*size_s;	// x,y+1
+
+				index[count++] = (size_s - 1) + (t+1)*size_s;	// x,y+1
+				index[count++] = 0 + (t*size_s);				// x+1,y
+				index[count++] = 0 + (t+1)*size_s;				// x+1,y+1
+			}
+
+			// Do the top and bottom caps, if necessary
+			if (path_open)
+			{
+				for (s = 0; s < size_s - 2; s++)
+				{
+					index[count++] = s+1;
+					index[count++] = s;
+					index[count++] = size_s - 1;
+				}
+
+				// We've got a top cap
+				S32 offset = (size_t - 1)*size_s;
+				for (s = 0; s < size_s - 2; s++)
+				{
+					// Inverted ordering from bottom cap.
+					index[count++] = offset + size_s - 1;
+					index[count++] = offset + s;
+					index[count++] = offset + s + 1;
+				}
+			}
+		}
+	}
+	else if (hollow)
+	{
+		// Closed hollow
+		// Outer face
+		
+		for (t = 0; t < size_t - 1; t++)
+		{
+			for (s = 0; s < size_s_out - 1; s++)
+			{
+				i  = s + t*size_s;
+
+				index[count++]  = i;				// x,y
+				index[count++]  = i + 1;			// x+1,y
+				index[count++]  = i + size_s;		// x,y+1
+
+				index[count++]  = i + size_s;		// x,y+1
+				index[count++]  = i + 1;			// x+1,y
+				index[count++]  = i + 1 + size_s;	// x+1,y+1
+			}
+		}
+
+		// Inner face
+		// Invert facing from outer face
+		for (t = 0; t < size_t - 1; t++)
+		{
+			for (s = size_s_out; s < size_s - 1; s++)
+			{
+				i  = s + t*size_s;
+
+				index[count++]  = i;				// x,y
+				index[count++]  = i + 1;			// x+1,y
+				index[count++]  = i + size_s;		// x,y+1
+
+				index[count++]  = i + size_s;		// x,y+1
+				index[count++]  = i + 1;			// x+1,y
+				index[count++]  = i + 1 + size_s;	// x+1,y+1
+			}
+		}
+
+		// Do the top and bottom caps, if necessary
+		if (path_open)
+		{
+			// Top cap
+			S32 pt1 = 0;
+			S32 pt2 = size_s-1;
+			S32 i   = (size_t - 1)*size_s;
+
+			while (pt2 - pt1 > 1)
+			{
+				// Use the profile points instead of the mesh, since you want
+				// the un-transformed profile distances.
+				LLVector3 p1 = getProfile().mProfile[pt1];
+				LLVector3 p2 = getProfile().mProfile[pt2];
+				LLVector3 pa = getProfile().mProfile[pt1+1];
+				LLVector3 pb = getProfile().mProfile[pt2-1];
+
+				p1.mV[VZ] = 0.f;
+				p2.mV[VZ] = 0.f;
+				pa.mV[VZ] = 0.f;
+				pb.mV[VZ] = 0.f;
+
+				// Use area of triangle to determine backfacing
+				F32 area_1a2, area_1ba, area_21b, area_2ab;
+				area_1a2 =  (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+							(pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+							(p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+				area_1ba =  (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+							(pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+							(pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+				area_21b =  (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+							(p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+				area_2ab =  (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+							(pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+				BOOL use_tri1a2 = TRUE;
+				BOOL tri_1a2 = TRUE;
+				BOOL tri_21b = TRUE;
+
+				if (area_1a2 < 0)
+				{
+					tri_1a2 = FALSE;
+				}
+				if (area_2ab < 0)
+				{
+					// Can't use, because it contains point b
+					tri_1a2 = FALSE;
+				}
+				if (area_21b < 0)
+				{
+					tri_21b = FALSE;
+				}
+				if (area_1ba < 0)
+				{
+					// Can't use, because it contains point b
+					tri_21b = FALSE;
+				}
+
+				if (!tri_1a2)
+				{
+					use_tri1a2 = FALSE;
+				}
+				else if (!tri_21b)
+				{
+					use_tri1a2 = TRUE;
+				}
+				else
+				{
+					LLVector3 d1 = p1 - pa;
+					LLVector3 d2 = p2 - pb;
+
+					if (d1.magVecSquared() < d2.magVecSquared())
+					{
+						use_tri1a2 = TRUE;
+					}
+					else
+					{
+						use_tri1a2 = FALSE;
+					}
+				}
+
+				if (use_tri1a2)
+				{
+					index[count++] = pt1 + i;
+					index[count++] = pt1 + 1 + i;
+					index[count++] = pt2 + i;
+					pt1++;
+				}
+				else
+				{
+					index[count++] = pt1 + i;
+					index[count++] = pt2 - 1 + i;
+					index[count++] = pt2 + i;
+					pt2--;
+				}
+			}
 
-			for (t = 0; t < size_t - 1; t++)
+			// Bottom cap
+			pt1          = 0;
+			pt2          = size_s-1;
+			while (pt2 - pt1 > 1)
 			{
-				// Outer face + 1 cut face
-				for (s = 0; s < size_s - 1; s++)
-				{
-					i  = s + t*size_s;
+				// Use the profile points instead of the mesh, since you want
+				// the un-transformed profile distances.
+				LLVector3 p1 = getProfile().mProfile[pt1];
+				LLVector3 p2 = getProfile().mProfile[pt2];
+				LLVector3 pa = getProfile().mProfile[pt1+1];
+				LLVector3 pb = getProfile().mProfile[pt2-1];
 
-					index[count++]  = i;				// x,y
-					index[count++]  = i + 1;			// x+1,y
-					index[count++]  = i + size_s;		// x,y+1
+				p1.mV[VZ] = 0.f;
+				p2.mV[VZ] = 0.f;
+				pa.mV[VZ] = 0.f;
+				pb.mV[VZ] = 0.f;
 
-					index[count++]  = i + size_s;		// x,y+1
-					index[count++]  = i + 1;			// x+1,y
-					index[count++]  = i + size_s + 1;	// x+1,y+1
-				}
+				// Use area of triangle to determine backfacing
+				F32 area_1a2, area_1ba, area_21b, area_2ab;
+				area_1a2 =  (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+							(pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+							(p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
 
-				// The other cut face
-				index[count++] = (size_s - 1) + (t*size_s);		// x,y
-				index[count++] = 0 + t*size_s;					// x+1,y
-				index[count++] = (size_s - 1) + (t+1)*size_s;	// x,y+1
+				area_1ba =  (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+							(pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+							(pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
 
-				index[count++] = (size_s - 1) + (t+1)*size_s;	// x,y+1
-				index[count++] = 0 + (t*size_s);				// x+1,y
-				index[count++] = 0 + (t+1)*size_s;				// x+1,y+1
-			}
+				area_21b =  (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+							(p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
 
-			// Do the top and bottom caps, if necessary
-			if (path_open)
-			{
-				for (s = 0; s < size_s - 2; s++)
+				area_2ab =  (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+							(pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+				BOOL use_tri1a2 = TRUE;
+				BOOL tri_1a2 = TRUE;
+				BOOL tri_21b = TRUE;
+
+				if (area_1a2 < 0)
 				{
-					index[count++] = s+1;
-					index[count++] = s;
-					index[count++] = size_s - 1;
+					tri_1a2 = FALSE;
+				}
+				if (area_2ab < 0)
+				{
+					// Can't use, because it contains point b
+					tri_1a2 = FALSE;
+				}
+				if (area_21b < 0)
+				{
+					tri_21b = FALSE;
+				}
+				if (area_1ba < 0)
+				{
+					// Can't use, because it contains point b
+					tri_21b = FALSE;
 				}
 
-				// We've got a top cap
-				S32 offset = (size_t - 1)*size_s;
-				for (s = 0; s < size_s - 2; s++)
+				if (!tri_1a2)
 				{
-					// Inverted ordering from bottom cap.
-					index[count++] = offset + size_s - 1;
-					index[count++] = offset + s;
-					index[count++] = offset + s + 1;
+					use_tri1a2 = FALSE;
+				}
+				else if (!tri_21b)
+				{
+					use_tri1a2 = TRUE;
+				}
+				else
+				{
+					LLVector3 d1 = p1 - pa;
+					LLVector3 d2 = p2 - pb;
+
+					if (d1.magVecSquared() < d2.magVecSquared())
+					{
+						use_tri1a2 = TRUE;
+					}
+					else
+					{
+						use_tri1a2 = FALSE;
+					}
+				}
+
+				if (use_tri1a2)
+				{
+					index[count++] = pt1;
+					index[count++] = pt2;
+					index[count++] = pt1 + 1;
+					pt1++;
+				}
+				else
+				{
+					index[count++] = pt1;
+					index[count++] = pt2;
+					index[count++] = pt2 - 1;
+					pt2--;
 				}
 			}
-		}
+		}		
 	}
-	else if (hollow)
+	else
 	{
-		// Closed hollow
-		// Outer face
-		
+		// Closed solid.  Easy case.
 		for (t = 0; t < size_t - 1; t++)
 		{
-			for (s = 0; s < size_s_out - 1; s++)
+			for (s = 0; s < size_s - 1; s++)
 			{
+				// Should wrap properly, but for now...
 				i  = s + t*size_s;
 
 				index[count++]  = i;				// x,y
@@ -3024,1436 +4011,1996 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 				index[count++]  = i + size_s;		// x,y+1
 				index[count++]  = i + 1;			// x+1,y
-				index[count++]  = i + 1 + size_s;	// x+1,y+1
+				index[count++]  = i + size_s + 1;	// x+1,y+1
 			}
 		}
 
-		// Inner face
-		// Invert facing from outer face
-		for (t = 0; t < size_t - 1; t++)
+		// Do the top and bottom caps, if necessary
+		if (path_open)
 		{
-			for (s = size_s_out; s < size_s - 1; s++)
+			// bottom cap
+			for (s = 1; s < size_s - 2; s++)
 			{
-				i  = s + t*size_s;
+				index[count++] = s+1;
+				index[count++] = s;
+				index[count++] = 0;
+			}
+
+			// top cap
+			S32 offset = (size_t - 1)*size_s;
+			for (s = 1; s < size_s - 2; s++)
+			{
+				// Inverted ordering from bottom cap.
+				index[count++] = offset;
+				index[count++] = offset + s;
+				index[count++] = offset + s + 1;
+			}
+		}
+	}
+
+#ifdef LL_DEBUG
+	// assert that we computed the correct number of indices
+	if (count != expected_num_triangle_indices )
+	{
+		llerrs << "bad index count prediciton:"
+			<< "  expected=" << expected_num_triangle_indices 
+			<< " actual=" << count << llendl;
+	}
+#endif
+
+#if 0
+	// verify that each index does not point beyond the size of the mesh
+	S32 num_vertices = mMesh.size();
+	for (i = 0; i < count; i+=3)
+	{
+		llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
+		llassert(index[i] < num_vertices);
+		llassert(index[i+1] < num_vertices);
+		llassert(index[i+2] < num_vertices);
+	}
+#endif
+
+	num_indices = count;
+	return index;
+}
+
+S32 LLVolume::getNumTriangleIndices() const
+{
+	BOOL profile_open = getProfile().isOpen();
+	BOOL hollow = (mParams.getProfileParams().getHollow() > 0);
+	BOOL path_open = getPath().isOpen();
+
+	S32 size_s, size_s_out, size_t;
+	size_s = getProfile().getTotal();
+	size_s_out = getProfile().getTotalOut();
+	size_t = getPath().mPath.size();
+
+	S32 count = 0;
+	if (profile_open)		/* Flawfinder: ignore */
+	{
+		if (hollow)
+		{
+			// Open hollow -- much like the closed solid, except we 
+			// we need to stitch up the gap between s=0 and s=size_s-1
+			count = (size_t - 1) * (((size_s -1) * 6) + 6);
+		}
+		else
+		{
+			count = (size_t - 1) * (((size_s -1) * 6) + 6); 
+		}
+	}
+	else if (hollow)
+	{
+		// Closed hollow
+		// Outer face
+		count = (size_t - 1) * (size_s_out - 1) * 6;
+
+		// Inner face
+		count += (size_t - 1) * ((size_s - 1) - size_s_out) * 6;
+	}
+	else
+	{
+		// Closed solid.  Easy case.
+		count = (size_t - 1) * (size_s - 1) * 6;
+	}
+
+	if (path_open)
+	{
+		S32 cap_triangle_count = size_s - 3;
+		if ( profile_open
+			|| hollow )
+		{
+			cap_triangle_count = size_s - 2;
+		}
+		if ( cap_triangle_count > 0 )
+		{
+			// top and bottom caps
+			count += cap_triangle_count * 2 * 3;
+		}
+	}
+	return count;
+}
+
+
+S32 LLVolume::getNumTriangles() const
+{
+	U32 triangle_count = 0;
 
-				index[count++]  = i;				// x,y
-				index[count++]  = i + 1;			// x+1,y
-				index[count++]  = i + size_s;		// x,y+1
+	for (S32 i = 0; i < getNumVolumeFaces(); ++i)
+	{
+		triangle_count += getVolumeFace(i).mNumIndices/3;
+	}
 
-				index[count++]  = i + size_s;		// x,y+1
-				index[count++]  = i + 1;			// x+1,y
-				index[count++]  = i + 1 + size_s;	// x+1,y+1
-			}
-		}
+	return triangle_count;
+}
 
-		// Do the top and bottom caps, if necessary
-		if (path_open)
-		{
-			// Top cap
-			S32 pt1 = 0;
-			S32 pt2 = size_s-1;
-			S32 i   = (size_t - 1)*size_s;
 
-			while (pt2 - pt1 > 1)
-			{
-				// Use the profile points instead of the mesh, since you want
-				// the un-transformed profile distances.
-				LLVector3 p1 = getProfile().mProfile[pt1];
-				LLVector3 p2 = getProfile().mProfile[pt2];
-				LLVector3 pa = getProfile().mProfile[pt1+1];
-				LLVector3 pb = getProfile().mProfile[pt2-1];
+//-----------------------------------------------------------------------------
+// generateSilhouetteVertices()
+//-----------------------------------------------------------------------------
+void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
+										  std::vector<LLVector3> &normals,
+										  const LLVector3& obj_cam_vec_in,
+										  const LLMatrix4& mat_in,
+										  const LLMatrix3& norm_mat_in,
+										  S32 face_mask)
+{
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
 
-				p1.mV[VZ] = 0.f;
-				p2.mV[VZ] = 0.f;
-				pa.mV[VZ] = 0.f;
-				pb.mV[VZ] = 0.f;
+	LLMatrix4a mat;
+	mat.loadu(mat_in);
 
-				// Use area of triangle to determine backfacing
-				F32 area_1a2, area_1ba, area_21b, area_2ab;
-				area_1a2 =  (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
-							(pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
-							(p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+	LLMatrix4a norm_mat;
+	norm_mat.loadu(norm_mat_in);
+		
+	LLVector4a obj_cam_vec;
+	obj_cam_vec.load3(obj_cam_vec_in.mV);
 
-				area_1ba =  (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
-							(pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
-							(pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+	vertices.clear();
+	normals.clear();
 
-				area_21b =  (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
-							(p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
-							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+	if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+	{
+		return;
+	}
+	
+	S32 cur_index = 0;
+	//for each face
+	for (face_list_t::iterator iter = mVolumeFaces.begin();
+		 iter != mVolumeFaces.end(); ++iter)
+	{
+		LLVolumeFace& face = *iter;
+	
+		if (!(face_mask & (0x1 << cur_index++)) ||
+		     face.mNumIndices == 0 || face.mEdge.empty())
+		{
+			continue;
+		}
 
-				area_2ab =  (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
-							(pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
-							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+		if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
+	
+		}
+		else {
 
-				BOOL use_tri1a2 = TRUE;
-				BOOL tri_1a2 = TRUE;
-				BOOL tri_21b = TRUE;
+			//==============================================
+			//DEBUG draw edge map instead of silhouette edge
+			//==============================================
 
-				if (area_1a2 < 0)
-				{
-					tri_1a2 = FALSE;
-				}
-				if (area_2ab < 0)
-				{
-					// Can't use, because it contains point b
-					tri_1a2 = FALSE;
-				}
-				if (area_21b < 0)
-				{
-					tri_21b = FALSE;
-				}
-				if (area_1ba < 0)
-				{
-					// Can't use, because it contains point b
-					tri_21b = FALSE;
-				}
+#if DEBUG_SILHOUETTE_EDGE_MAP
 
-				if (!tri_1a2)
-				{
-					use_tri1a2 = FALSE;
-				}
-				else if (!tri_21b)
-				{
-					use_tri1a2 = TRUE;
-				}
-				else
-				{
-					LLVector3 d1 = p1 - pa;
-					LLVector3 d2 = p2 - pb;
+			//for each triangle
+			U32 count = face.mNumIndices;
+			for (U32 j = 0; j < count/3; j++) {
+				//get vertices
+				S32 v1 = face.mIndices[j*3+0];
+				S32 v2 = face.mIndices[j*3+1];
+				S32 v3 = face.mIndices[j*3+2];
 
-					if (d1.magVecSquared() < d2.magVecSquared())
-					{
-						use_tri1a2 = TRUE;
+				//get current face center
+				LLVector3 cCenter = (face.mVertices[v1].getPosition() + 
+									face.mVertices[v2].getPosition() + 
+									face.mVertices[v3].getPosition()) / 3.0f;
+
+				//for each edge
+				for (S32 k = 0; k < 3; k++) {
+                    S32 nIndex = face.mEdge[j*3+k];
+					if (nIndex <= -1) {
+						continue;
 					}
-					else
-					{
-						use_tri1a2 = FALSE;
+
+					if (nIndex >= (S32) count/3) {
+						continue;
 					}
-				}
+					//get neighbor vertices
+					v1 = face.mIndices[nIndex*3+0];
+					v2 = face.mIndices[nIndex*3+1];
+					v3 = face.mIndices[nIndex*3+2];
 
-				if (use_tri1a2)
-				{
-					index[count++] = pt1 + i;
-					index[count++] = pt1 + 1 + i;
-					index[count++] = pt2 + i;
-					pt1++;
-				}
-				else
-				{
-					index[count++] = pt1 + i;
-					index[count++] = pt2 - 1 + i;
-					index[count++] = pt2 + i;
-					pt2--;
+					//get neighbor face center
+					LLVector3 nCenter = (face.mVertices[v1].getPosition() + 
+									face.mVertices[v2].getPosition() + 
+									face.mVertices[v3].getPosition()) / 3.0f;
+
+					//draw line
+					vertices.push_back(cCenter);
+					vertices.push_back(nCenter);
+					normals.push_back(LLVector3(1,1,1));
+					normals.push_back(LLVector3(1,1,1));
+					segments.push_back(vertices.size());
 				}
 			}
+		
+			continue;
 
-			// Bottom cap
-			pt1          = 0;
-			pt2          = size_s-1;
-			while (pt2 - pt1 > 1)
-			{
-				// Use the profile points instead of the mesh, since you want
-				// the un-transformed profile distances.
-				LLVector3 p1 = getProfile().mProfile[pt1];
-				LLVector3 p2 = getProfile().mProfile[pt2];
-				LLVector3 pa = getProfile().mProfile[pt1+1];
-				LLVector3 pb = getProfile().mProfile[pt2-1];
+			//==============================================
+			//DEBUG
+			//==============================================
 
-				p1.mV[VZ] = 0.f;
-				p2.mV[VZ] = 0.f;
-				pa.mV[VZ] = 0.f;
-				pb.mV[VZ] = 0.f;
+			//==============================================
+			//DEBUG draw normals instead of silhouette edge
+			//==============================================
+#elif DEBUG_SILHOUETTE_NORMALS
 
-				// Use area of triangle to determine backfacing
-				F32 area_1a2, area_1ba, area_21b, area_2ab;
-				area_1a2 =  (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
-							(pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
-							(p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+			//for each vertex
+			for (U32 j = 0; j < face.mNumVertices; j++) {
+				vertices.push_back(face.mVertices[j].getPosition());
+				vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f);
+				normals.push_back(LLVector3(0,0,1));
+				normals.push_back(LLVector3(0,0,1));
+				segments.push_back(vertices.size());
+#if DEBUG_SILHOUETTE_BINORMALS
+				vertices.push_back(face.mVertices[j].getPosition());
+				vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mBinormal*0.1f);
+				normals.push_back(LLVector3(0,0,1));
+				normals.push_back(LLVector3(0,0,1));
+				segments.push_back(vertices.size());
+#endif
+			}
+						
+			continue;
+#else
+			//==============================================
+			//DEBUG
+			//==============================================
 
-				area_1ba =  (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
-							(pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
-							(pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+			static const U8 AWAY = 0x01,
+							TOWARDS = 0x02;
 
-				area_21b =  (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
-							(p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
-							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+			//for each triangle
+			std::vector<U8> fFacing;
+			vector_append(fFacing, face.mNumIndices/3);
 
-				area_2ab =  (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
-							(pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
-							(pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+			LLVector4a* v = (LLVector4a*) face.mPositions;
+			LLVector4a* n = (LLVector4a*) face.mNormals;
 
-				BOOL use_tri1a2 = TRUE;
-				BOOL tri_1a2 = TRUE;
-				BOOL tri_21b = TRUE;
+			for (U32 j = 0; j < face.mNumIndices/3; j++) 
+			{
+				//approximate normal
+				S32 v1 = face.mIndices[j*3+0];
+				S32 v2 = face.mIndices[j*3+1];
+				S32 v3 = face.mIndices[j*3+2];
 
-				if (area_1a2 < 0)
-				{
-					tri_1a2 = FALSE;
-				}
-				if (area_2ab < 0)
-				{
-					// Can't use, because it contains point b
-					tri_1a2 = FALSE;
-				}
-				if (area_21b < 0)
-				{
-					tri_21b = FALSE;
-				}
-				if (area_1ba < 0)
-				{
-					// Can't use, because it contains point b
-					tri_21b = FALSE;
-				}
+				LLVector4a c1,c2;
+				c1.setSub(v[v1], v[v2]);
+				c2.setSub(v[v2], v[v3]);
 
-				if (!tri_1a2)
-				{
-					use_tri1a2 = FALSE;
-				}
-				else if (!tri_21b)
+				LLVector4a norm;
+
+				norm.setCross3(c1, c2);
+
+				if (norm.dot3(norm) < 0.00000001f) 
 				{
-					use_tri1a2 = TRUE;
+					fFacing[j] = AWAY | TOWARDS;
 				}
-				else
+				else 
 				{
-					LLVector3 d1 = p1 - pa;
-					LLVector3 d2 = p2 - pb;
-
-					if (d1.magVecSquared() < d2.magVecSquared())
+					//get view vector
+					LLVector4a view;
+					view.setSub(obj_cam_vec, v[v1]);
+					bool away = view.dot3(norm) > 0.0f; 
+					if (away) 
 					{
-						use_tri1a2 = TRUE;
+						fFacing[j] = AWAY;
 					}
-					else
+					else 
 					{
-						use_tri1a2 = FALSE;
+						fFacing[j] = TOWARDS;
 					}
 				}
-
-				if (use_tri1a2)
-				{
-					index[count++] = pt1;
-					index[count++] = pt2;
-					index[count++] = pt1 + 1;
-					pt1++;
-				}
-				else
-				{
-					index[count++] = pt1;
-					index[count++] = pt2;
-					index[count++] = pt2 - 1;
-					pt2--;
+			}
+			
+			//for each triangle
+			for (U32 j = 0; j < face.mNumIndices/3; j++) 
+			{
+				if (fFacing[j] == (AWAY | TOWARDS)) 
+				{ //this is a degenerate triangle
+					//take neighbor facing (degenerate faces get facing of one of their neighbors)
+					// *FIX IF NEEDED:  this does not deal with neighboring degenerate faces
+					for (S32 k = 0; k < 3; k++) 
+					{
+						S32 index = face.mEdge[j*3+k];
+						if (index != -1) 
+						{
+							fFacing[j] = fFacing[index];
+							break;
+						}
+					}
+					continue; //skip degenerate face
 				}
+
+				//for each edge
+				for (S32 k = 0; k < 3; k++) {
+					S32 index = face.mEdge[j*3+k];
+					if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
+						//our neighbor is degenerate, make him face our direction
+						fFacing[face.mEdge[j*3+k]] = fFacing[j];
+						continue;
+					}
+
+					if (index == -1 ||		//edge has no neighbor, MUST be a silhouette edge
+						(fFacing[index] & fFacing[j]) == 0) { 	//we found a silhouette edge
+
+						S32 v1 = face.mIndices[j*3+k];
+						S32 v2 = face.mIndices[j*3+((k+1)%3)];
+						
+						LLVector4a t;
+						mat.affineTransform(v[v1], t);
+						vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+						norm_mat.rotate(n[v1], t);
+
+						t.normalize3fast();
+						normals.push_back(LLVector3(t[0], t[1], t[2]));
+
+						mat.affineTransform(v[v2], t);
+						vertices.push_back(LLVector3(t[0], t[1], t[2]));
+						
+						norm_mat.rotate(n[v2], t);
+						t.normalize3fast();
+						normals.push_back(LLVector3(t[0], t[1], t[2]));
+					}
+				}		
 			}
-		}		
+#endif
+		}
+	}
+}
+
+S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+								   S32 face,
+								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+{
+	LLVector4a starta, enda;
+	starta.load3(start.mV);
+	enda.load3(end.mV);
+
+	return lineSegmentIntersect(starta, enda, face, intersection, tex_coord, normal, bi_normal);
+
+}
+
+
+S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
+								   S32 face,
+								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+{
+	S32 hit_face = -1;
+	
+	S32 start_face;
+	S32 end_face;
+	
+	if (face == -1) // ALL_SIDES
+	{
+		start_face = 0;
+		end_face = getNumVolumeFaces() - 1;
 	}
 	else
 	{
-		// Closed solid.  Easy case.
-		for (t = 0; t < size_t - 1; t++)
-		{
-			for (s = 0; s < size_s - 1; s++)
-			{
-				// Should wrap properly, but for now...
-				i  = s + t*size_s;
+		start_face = face;
+		end_face = face;
+	}
 
-				index[count++]  = i;				// x,y
-				index[count++]  = i + 1;			// x+1,y
-				index[count++]  = i + size_s;		// x,y+1
+	LLVector4a dir;
+	dir.setSub(end, start);
 
-				index[count++]  = i + size_s;		// x,y+1
-				index[count++]  = i + 1;			// x+1,y
-				index[count++]  = i + size_s + 1;	// x+1,y+1
-			}
-		}
+	F32 closest_t = 2.f; // must be larger than 1
+	
+	end_face = llmin(end_face, getNumVolumeFaces()-1);
 
-		// Do the top and bottom caps, if necessary
-		if (path_open)
+	for (S32 i = start_face; i <= end_face; i++)
+	{
+		LLVolumeFace &face = mVolumeFaces[i];
+
+		LLVector4a box_center;
+		box_center.setAdd(face.mExtents[0], face.mExtents[1]);
+		box_center.mul(0.5f);
+
+		LLVector4a box_size;
+		box_size.setSub(face.mExtents[1], face.mExtents[0]);
+
+        if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
 		{
-			// bottom cap
-			for (s = 1; s < size_s - 2; s++)
+			if (bi_normal != NULL) // if the caller wants binormals, we may need to generate them
 			{
-				index[count++] = s+1;
-				index[count++] = s;
-				index[count++] = 0;
+				genBinormals(i);
 			}
 
-			// top cap
-			S32 offset = (size_t - 1)*size_s;
-			for (s = 1; s < size_s - 2; s++)
+			if (!face.mOctree)
 			{
-				// Inverted ordering from bottom cap.
-				index[count++] = offset;
-				index[count++] = offset + s;
-				index[count++] = offset + s + 1;
+				face.createOctree();
 			}
-		}
-	}
+			
+			//LLVector4a* p = (LLVector4a*) face.mPositions;
 
-#ifdef LL_DEBUG
-	// assert that we computed the correct number of indices
-	if (count != expected_num_triangle_indices )
-	{
-		llerrs << "bad index count prediciton:"
-			<< "  expected=" << expected_num_triangle_indices 
-			<< " actual=" << count << llendl;
+			LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, bi_normal);
+			intersect.traverse(face.mOctree);
+			if (intersect.mHitFace)
+			{
+				hit_face = i;
+			}
+		}		
 	}
-#endif
+	
+	
+	return hit_face;
+}
 
-#if 0
-	// verify that each index does not point beyond the size of the mesh
-	S32 num_vertices = mMesh.size();
-	for (i = 0; i < count; i+=3)
-	{
-		llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
-		llassert(index[i] < num_vertices);
-		llassert(index[i+1] < num_vertices);
-		llassert(index[i+2] < num_vertices);
-	}
-#endif
+class LLVertexIndexPair
+{
+public:
+	LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
 
-	num_indices = count;
-	return index;
-}
+	LLVector3 mVertex;
+	S32	mIndex;
+};
 
-S32 LLVolume::getNumTriangleIndices() const
+LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
 {
-	BOOL profile_open = getProfile().isOpen();
-	BOOL hollow = (mParams.getProfileParams().getHollow() > 0);
-	BOOL path_open = getPath().isOpen();
+	mVertex = vertex;
+	mIndex = index;
+}
 
-	S32 size_s, size_s_out, size_t;
-	size_s = getProfile().getTotal();
-	size_s_out = getProfile().getTotalOut();
-	size_t = getPath().mPath.size();
+const F32 VERTEX_SLOP = 0.00001f;
+const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
 
-	S32 count = 0;
-	if (profile_open)		/* Flawfinder: ignore */
+struct lessVertex
+{
+	bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
 	{
-		if (hollow)
+		const F32 slop = VERTEX_SLOP;
+
+		if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
 		{
-			// Open hollow -- much like the closed solid, except we 
-			// we need to stitch up the gap between s=0 and s=size_s-1
-			count = (size_t - 1) * (((size_s -1) * 6) + 6);
+			return TRUE;
 		}
-		else
+		else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
 		{
-			count = (size_t - 1) * (((size_s -1) * 6) + 6); 
+			return FALSE;
+		}
+		
+		if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
+		{
+			return TRUE;
+		}
+		else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
+		{
+			return FALSE;
+		}
+		
+		if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
+		{
+			return TRUE;
+		}
+		else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
+		{
+			return FALSE;
 		}
+		
+		return FALSE;
 	}
-	else if (hollow)
-	{
-		// Closed hollow
-		// Outer face
-		count = (size_t - 1) * (size_s_out - 1) * 6;
+};
 
-		// Inner face
-		count += (size_t - 1) * ((size_s - 1) - size_s_out) * 6;
-	}
-	else
+struct lessTriangle
+{
+	bool operator()(const S32 *a, const S32 *b)
 	{
-		// Closed solid.  Easy case.
-		count = (size_t - 1) * (size_s - 1) * 6;
-	}
+		if (*a < *b)
+		{
+			return TRUE;
+		}
+		else if (*a > *b)
+		{
+			return FALSE;
+		}
 
-	if (path_open)
-	{
-		S32 cap_triangle_count = size_s - 3;
-		if ( profile_open
-			|| hollow )
+		if (*(a+1) < *(b+1))
+		{
+			return TRUE;
+		}
+		else if (*(a+1) > *(b+1))
+		{
+			return FALSE;
+		}
+
+		if (*(a+2) < *(b+2))
 		{
-			cap_triangle_count = size_s - 2;
+			return TRUE;
 		}
-		if ( cap_triangle_count > 0 )
+		else if (*(a+2) > *(b+2))
 		{
-			// top and bottom caps
-			count += cap_triangle_count * 2 * 3;
+			return FALSE;
 		}
+
+		return FALSE;
 	}
-	return count;
+};
+
+BOOL equalTriangle(const S32 *a, const S32 *b)
+{
+	if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
+	{
+		return TRUE;
+	}
+	return FALSE;
 }
 
-//-----------------------------------------------------------------------------
-// generateSilhouetteVertices()
-//-----------------------------------------------------------------------------
-void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
-										  std::vector<LLVector3> &normals,
-										  std::vector<S32> &segments,
-										  const LLVector3& obj_cam_vec,
-										  const LLMatrix4& mat,
-										  const LLMatrix3& norm_mat,
-										  S32 face_mask)
+BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
+									const std::vector<Point>& input_vertices,
+									const S32 num_input_triangles,
+									S32 *input_triangles,
+									S32 &num_output_vertices,
+									LLVector3 **output_vertices,
+									S32 &num_output_triangles,
+									S32 **output_triangles)
 {
 	LLMemType m1(LLMemType::MTYPE_VOLUME);
 	
-	vertices.clear();
-	normals.clear();
-	segments.clear();
-
-	S32 cur_index = 0;
-	//for each face
-	for (face_list_t::iterator iter = mVolumeFaces.begin();
-		 iter != mVolumeFaces.end(); ++iter)
+	/* Testing: avoid any cleanup
+	static BOOL skip_cleanup = TRUE;
+	if ( skip_cleanup )
 	{
-		const LLVolumeFace& face = *iter;
-	
-		if (!(face_mask & (0x1 << cur_index++)))
+		num_output_vertices = num_input_vertices;
+		num_output_triangles = num_input_triangles;
+
+		*output_vertices = new LLVector3[num_input_vertices];
+		for (S32 index = 0; index < num_input_vertices; index++)
 		{
-			continue;
-		}
-		if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
-	
+			(*output_vertices)[index] = input_vertices[index].mPos;
 		}
-		else {
-
-			//==============================================
-			//DEBUG draw edge map instead of silhouette edge
-			//==============================================
-
-#if DEBUG_SILHOUETTE_EDGE_MAP
 
-			//for each triangle
-			U32 count = face.mIndices.size();
-			for (U32 j = 0; j < count/3; j++) {
-				//get vertices
-				S32 v1 = face.mIndices[j*3+0];
-				S32 v2 = face.mIndices[j*3+1];
-				S32 v3 = face.mIndices[j*3+2];
-
-				//get current face center
-				LLVector3 cCenter = (face.mVertices[v1].mPosition + 
-									face.mVertices[v2].mPosition + 
-									face.mVertices[v3].mPosition) / 3.0f;
-
-				//for each edge
-				for (S32 k = 0; k < 3; k++) {
-                    S32 nIndex = face.mEdge[j*3+k];
-					if (nIndex <= -1) {
-						continue;
-					}
-
-					if (nIndex >= (S32) count/3) {
-						continue;
-					}
-					//get neighbor vertices
-					v1 = face.mIndices[nIndex*3+0];
-					v2 = face.mIndices[nIndex*3+1];
-					v3 = face.mIndices[nIndex*3+2];
-
-					//get neighbor face center
-					LLVector3 nCenter = (face.mVertices[v1].mPosition + 
-									face.mVertices[v2].mPosition + 
-									face.mVertices[v3].mPosition) / 3.0f;
+		*output_triangles = new S32[num_input_triangles*3];
+		memcpy(*output_triangles, input_triangles, 3*num_input_triangles*sizeof(S32));		// Flawfinder: ignore
+		return TRUE;
+	}
+	*/
 
-					//draw line
-					vertices.push_back(cCenter);
-					vertices.push_back(nCenter);
-					normals.push_back(LLVector3(1,1,1));
-					normals.push_back(LLVector3(1,1,1));
-					segments.push_back(vertices.size());
-				}
-			}
-		
-			continue;
+	// Here's how we do this:
+	// Create a structure which contains the original vertex index and the
+	// LLVector3 data.
+	// "Sort" the data by the vectors
+	// Create an array the size of the old vertex list, with a mapping of
+	// old indices to new indices.
+	// Go through triangles, shift so the lowest index is first
+	// Sort triangles by first index
+	// Remove duplicate triangles
+	// Allocate and pack new triangle data.
 
-			//==============================================
-			//DEBUG
-			//==============================================
+	//LLTimer cleanupTimer;
+	//llinfos << "In vertices: " << num_input_vertices << llendl;
+	//llinfos << "In triangles: " << num_input_triangles << llendl;
 
-			//==============================================
-			//DEBUG draw normals instead of silhouette edge
-			//==============================================
-#elif DEBUG_SILHOUETTE_NORMALS
+	S32 i;
+	typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
+	vertex_set_t vertex_list;
 
-			//for each vertex
-			for (U32 j = 0; j < face.mVertices.size(); j++) {
-				vertices.push_back(face.mVertices[j].mPosition);
-				vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mNormal*0.1f);
-				normals.push_back(LLVector3(0,0,1));
-				normals.push_back(LLVector3(0,0,1));
-				segments.push_back(vertices.size());
-#if DEBUG_SILHOUETTE_BINORMALS
-				vertices.push_back(face.mVertices[j].mPosition);
-				vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mBinormal*0.1f);
-				normals.push_back(LLVector3(0,0,1));
-				normals.push_back(LLVector3(0,0,1));
-				segments.push_back(vertices.size());
-#endif
-			}
-						
-			continue;
-#else
-			//==============================================
-			//DEBUG
-			//==============================================
+	LLVertexIndexPair *pairp = NULL;
+	for (i = 0; i < num_input_vertices; i++)
+	{
+		LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
+		vertex_list.insert(new_pairp);
+	}
 
-			static const U8 AWAY = 0x01,
-							TOWARDS = 0x02;
+	// Generate the vertex mapping and the list of vertices without
+	// duplicates.  This will crash if there are no vertices.
+	llassert(num_input_vertices > 0); // check for no vertices!
+	S32 *vertex_mapping = new S32[num_input_vertices];
+	LLVector3 *new_vertices = new LLVector3[num_input_vertices];
+	LLVertexIndexPair *prev_pairp = NULL;
 
-			//for each triangle
-			std::vector<U8> fFacing;
-			vector_append(fFacing, face.mIndices.size()/3);
-			for (U32 j = 0; j < face.mIndices.size()/3; j++) 
-			{
-				//approximate normal
-				S32 v1 = face.mIndices[j*3+0];
-				S32 v2 = face.mIndices[j*3+1];
-				S32 v3 = face.mIndices[j*3+2];
+	S32 new_num_vertices;
 
-				LLVector3 norm = (face.mVertices[v1].mPosition - face.mVertices[v2].mPosition) % 
-					(face.mVertices[v2].mPosition - face.mVertices[v3].mPosition);
-				
-				if (norm.magVecSquared() < 0.00000001f) 
-				{
-					fFacing[j] = AWAY | TOWARDS;
-				}
-				else 
-				{
-					//get view vector
-					LLVector3 view = (obj_cam_vec-face.mVertices[v1].mPosition);
-					bool away = view * norm > 0.0f; 
-					if (away) 
-					{
-						fFacing[j] = AWAY;
-					}
-					else 
-					{
-						fFacing[j] = TOWARDS;
-					}
-				}
-			}
-			
-			//for each triangle
-			for (U32 j = 0; j < face.mIndices.size()/3; j++) 
-			{
-				if (fFacing[j] == (AWAY | TOWARDS)) 
-				{ //this is a degenerate triangle
-					//take neighbor facing (degenerate faces get facing of one of their neighbors)
-					// *FIX IF NEEDED:  this does not deal with neighboring degenerate faces
-					for (S32 k = 0; k < 3; k++) 
-					{
-						S32 index = face.mEdge[j*3+k];
-						if (index != -1) 
-						{
-							fFacing[j] = fFacing[index];
-							break;
-						}
-					}
-					continue; //skip degenerate face
-				}
+	new_num_vertices = 0;
+	for (vertex_set_t::iterator iter = vertex_list.begin(),
+			 end = vertex_list.end();
+		 iter != end; iter++)
+	{
+		pairp = *iter;
+		if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))	
+		{
+			new_vertices[new_num_vertices] = pairp->mVertex;
+			//llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
+			new_num_vertices++;
+			// Update the previous
+			prev_pairp = pairp;
+		}
+		else
+		{
+			//llinfos << "Removed duplicate vertex " << pairp->mVertex << ", distance magVecSquared() is " << (pairp->mVertex - prev_pairp->mVertex).magVecSquared() << llendl;
+		}
+		vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
+	}
 
-				//for each edge
-				for (S32 k = 0; k < 3; k++) {
-					S32 index = face.mEdge[j*3+k];
-					if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
-						//our neighbor is degenerate, make him face our direction
-						fFacing[face.mEdge[j*3+k]] = fFacing[j];
-						continue;
-					}
+	// Iterate through triangles and remove degenerates, re-ordering vertices
+	// along the way.
+	S32 *new_triangles = new S32[num_input_triangles * 3];
+	S32 new_num_triangles = 0;
 
-					if (index == -1 ||		//edge has no neighbor, MUST be a silhouette edge
-						(fFacing[index] & fFacing[j]) == 0) { 	//we found a silhouette edge
+	for (i = 0; i < num_input_triangles; i++)
+	{
+		S32 v1 = i*3;
+		S32 v2 = v1 + 1;
+		S32 v3 = v1 + 2;
 
-						S32 v1 = face.mIndices[j*3+k];
-						S32 v2 = face.mIndices[j*3+((k+1)%3)];
-						
-						vertices.push_back(face.mVertices[v1].mPosition*mat);
-						LLVector3 norm1 = face.mVertices[v1].mNormal * norm_mat;
-						norm1.normVec();
-						normals.push_back(norm1);
+		//llinfos << "Checking triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
+		input_triangles[v1] = vertex_mapping[input_triangles[v1]];
+		input_triangles[v2] = vertex_mapping[input_triangles[v2]];
+		input_triangles[v3] = vertex_mapping[input_triangles[v3]];
 
-						vertices.push_back(face.mVertices[v2].mPosition*mat);
-						LLVector3 norm2 = face.mVertices[v2].mNormal * norm_mat;
-						norm2.normVec();
-						normals.push_back(norm2);
+		if ((input_triangles[v1] == input_triangles[v2])
+			|| (input_triangles[v1] == input_triangles[v3])
+			|| (input_triangles[v2] == input_triangles[v3]))
+		{
+			//llinfos << "Removing degenerate triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
+			// Degenerate triangle, skip
+			continue;
+		}
 
-						segments.push_back(vertices.size());
-					}
-				}		
+		if (input_triangles[v1] < input_triangles[v2])
+		{
+			if (input_triangles[v1] < input_triangles[v3])
+			{
+				// (0 < 1) && (0 < 2)
+				new_triangles[new_num_triangles*3] = input_triangles[v1];
+				new_triangles[new_num_triangles*3+1] = input_triangles[v2];
+				new_triangles[new_num_triangles*3+2] = input_triangles[v3];
+			}
+			else
+			{
+				// (0 < 1) && (2 < 0)
+				new_triangles[new_num_triangles*3] = input_triangles[v3];
+				new_triangles[new_num_triangles*3+1] = input_triangles[v1];
+				new_triangles[new_num_triangles*3+2] = input_triangles[v2];
 			}
-#endif
 		}
+		else if (input_triangles[v2] < input_triangles[v3])
+		{
+			// (1 < 0) && (1 < 2)
+			new_triangles[new_num_triangles*3] = input_triangles[v2];
+			new_triangles[new_num_triangles*3+1] = input_triangles[v3];
+			new_triangles[new_num_triangles*3+2] = input_triangles[v1];
+		}
+		else
+		{
+			// (1 < 0) && (2 < 1)
+			new_triangles[new_num_triangles*3] = input_triangles[v3];
+			new_triangles[new_num_triangles*3+1] = input_triangles[v1];
+			new_triangles[new_num_triangles*3+2] = input_triangles[v2];
+		}
+		new_num_triangles++;
 	}
-}
 
-S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
-								   S32 face,
-								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
-{
-	S32 hit_face = -1;
-	
-	S32 start_face;
-	S32 end_face;
-	
-	if (face == -1) // ALL_SIDES
-	{
-		start_face = 0;
-		end_face = getNumVolumeFaces() - 1;
-	}
-	else
+	if (new_num_triangles == 0)
 	{
-		start_face = face;
-		end_face = face;
+		llwarns << "Created volume object with 0 faces." << llendl;
+		delete[] new_triangles;
+		delete[] vertex_mapping;
+		delete[] new_vertices;
+		return FALSE;
 	}
 
-	LLVector3 dir = end - start;
+	typedef std::set<S32*, lessTriangle> triangle_set_t;
+	triangle_set_t triangle_list;
 
-	F32 closest_t = 2.f; // must be larger than 1
-	
-	for (S32 i = start_face; i <= end_face; i++)
+	for (i = 0; i < new_num_triangles; i++)
 	{
-		const LLVolumeFace &face = getVolumeFace((U32)i);
+		triangle_list.insert(&new_triangles[i*3]);
+	}
+
+	// Sort through the triangle list, and delete duplicates
 
-		LLVector3 box_center = (face.mExtents[0] + face.mExtents[1]) / 2.f;
-		LLVector3 box_size   = face.mExtents[1] - face.mExtents[0];
+	S32 *prevp = NULL;
+	S32 *curp = NULL;
 
-        if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
+	S32 *sorted_tris = new S32[new_num_triangles*3];
+	S32 cur_tri = 0;
+	for (triangle_set_t::iterator iter = triangle_list.begin(),
+			 end = triangle_list.end();
+		 iter != end; iter++)
+	{
+		curp = *iter;
+		if (!prevp || !equalTriangle(prevp, curp))
 		{
-			if (bi_normal != NULL) // if the caller wants binormals, we may need to generate them
-			{
-				genBinormals(i);
-			}
-			
-			for (U32 tri = 0; tri < face.mIndices.size()/3; tri++) 
-			{
-				S32 index1 = face.mIndices[tri*3+0];
-				S32 index2 = face.mIndices[tri*3+1];
-				S32 index3 = face.mIndices[tri*3+2];
-
-				F32 a, b, t;
-			
-				if (LLTriangleRayIntersect(face.mVertices[index1].mPosition,
-										   face.mVertices[index2].mPosition,
-										   face.mVertices[index3].mPosition,
-										   start, dir, &a, &b, &t, FALSE))
-				{
-					if ((t >= 0.f) &&      // if hit is after start
-						(t <= 1.f) &&      // and before end
-						(t < closest_t))   // and this hit is closer
+			//llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+			sorted_tris[cur_tri*3] = *curp;
+			sorted_tris[cur_tri*3+1] = *(curp+1);
+			sorted_tris[cur_tri*3+2] = *(curp+2);
+			cur_tri++;
+			prevp = curp;
+		}
+		else
 		{
-						closest_t = t;
-						hit_face = i;
-
-						if (intersection != NULL)
-						{
-							*intersection = start + dir * closest_t;
-						}
-			
-						if (tex_coord != NULL)
-			{
-							*tex_coord = ((1.f - a - b)  * face.mVertices[index1].mTexCoord +
-										  a              * face.mVertices[index2].mTexCoord +
-										  b              * face.mVertices[index3].mTexCoord);
-
-						}
+			//llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+		}
+	}
 
-						if (normal != NULL)
-				{
-							*normal    = ((1.f - a - b)  * face.mVertices[index1].mNormal + 
-										  a              * face.mVertices[index2].mNormal +
-										  b              * face.mVertices[index3].mNormal);
-						}
+	*output_vertices = new LLVector3[new_num_vertices];
+	num_output_vertices = new_num_vertices;
+	for (i = 0; i < new_num_vertices; i++)
+	{
+		(*output_vertices)[i] = new_vertices[i];
+	}
 
-						if (bi_normal != NULL)
-					{
-							*bi_normal = ((1.f - a - b)  * face.mVertices[index1].mBinormal + 
-										  a              * face.mVertices[index2].mBinormal +
-										  b              * face.mVertices[index3].mBinormal);
-						}
+	*output_triangles = new S32[cur_tri*3];
+	num_output_triangles = cur_tri;
+	memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32));		/* Flawfinder: ignore */
 
-					}
-				}
-			}
-		}		
+	/*
+	llinfos << "Out vertices: " << num_output_vertices << llendl;
+	llinfos << "Out triangles: " << num_output_triangles << llendl;
+	for (i = 0; i < num_output_vertices; i++)
+	{
+		llinfos << i << ":" << (*output_vertices)[i] << llendl;
 	}
+	for (i = 0; i < num_output_triangles; i++)
+	{
+		llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
+	}
+	*/
+
+	//llinfos << "Out vertices: " << num_output_vertices << llendl;
+	//llinfos << "Out triangles: " << num_output_triangles << llendl;
+	delete[] vertex_mapping;
+	vertex_mapping = NULL;
+	delete[] new_vertices;
+	new_vertices = NULL;
+	delete[] new_triangles;
+	new_triangles = NULL;
+	delete[] sorted_tris;
+	sorted_tris = NULL;
+	triangle_list.clear();
+	std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
+	vertex_list.clear();
 	
-	
-	return hit_face;
+	return TRUE;
 }
 
-class LLVertexIndexPair
-{
-public:
-	LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
-
-	LLVector3 mVertex;
-	S32	mIndex;
-};
 
-LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
+BOOL LLVolumeParams::importFile(LLFILE *fp)
 {
-	mVertex = vertex;
-	mIndex = index;
-}
-
-const F32 VERTEX_SLOP = 0.00001f;
-const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	//llinfos << "importing volume" << llendl;
+	const S32 BUFSIZE = 16384;
+	char buffer[BUFSIZE];	/* Flawfinder: ignore */
+	// *NOTE: changing the size or type of this buffer will require
+	// changing the sscanf below.
+	char keyword[256];	/* Flawfinder: ignore */
+	keyword[0] = 0;
 
-struct lessVertex
-{
-	bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
+	while (!feof(fp))
 	{
-		const F32 slop = VERTEX_SLOP;
-
-		if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
+		if (fgets(buffer, BUFSIZE, fp) == NULL)
 		{
-			return TRUE;
+			buffer[0] = '\0';
 		}
-		else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
+		
+		sscanf(buffer, " %255s", keyword);	/* Flawfinder: ignore */
+		if (!strcmp("{", keyword))
 		{
-			return FALSE;
+			continue;
 		}
-		
-		if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
+		if (!strcmp("}",keyword))
 		{
-			return TRUE;
+			break;
 		}
-		else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
+		else if (!strcmp("profile", keyword))
 		{
-			return FALSE;
+			mProfileParams.importFile(fp);
 		}
-		
-		if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
+		else if (!strcmp("path",keyword))
 		{
-			return TRUE;
+			mPathParams.importFile(fp);
 		}
-		else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
+		else
 		{
-			return FALSE;
+			llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
 		}
-		
-		return FALSE;
 	}
-};
 
-struct lessTriangle
+	return TRUE;
+}
+
+BOOL LLVolumeParams::exportFile(LLFILE *fp) const
 {
-	bool operator()(const S32 *a, const S32 *b)
+	fprintf(fp,"\tshape 0\n");
+	fprintf(fp,"\t{\n");
+	mPathParams.exportFile(fp);
+	mProfileParams.exportFile(fp);
+	fprintf(fp, "\t}\n");
+	return TRUE;
+}
+
+
+BOOL LLVolumeParams::importLegacyStream(std::istream& input_stream)
+{
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	//llinfos << "importing volume" << llendl;
+	const S32 BUFSIZE = 16384;
+	// *NOTE: changing the size or type of this buffer will require
+	// changing the sscanf below.
+	char buffer[BUFSIZE];		/* Flawfinder: ignore */
+	char keyword[256];		/* Flawfinder: ignore */
+	keyword[0] = 0;
+
+	while (input_stream.good())
 	{
-		if (*a < *b)
-		{
-			return TRUE;
-		}
-		else if (*a > *b)
+		input_stream.getline(buffer, BUFSIZE);
+		sscanf(buffer, " %255s", keyword);
+		if (!strcmp("{", keyword))
 		{
-			return FALSE;
+			continue;
 		}
-
-		if (*(a+1) < *(b+1))
+		if (!strcmp("}",keyword))
 		{
-			return TRUE;
+			break;
 		}
-		else if (*(a+1) > *(b+1))
+		else if (!strcmp("profile", keyword))
 		{
-			return FALSE;
+			mProfileParams.importLegacyStream(input_stream);
 		}
-
-		if (*(a+2) < *(b+2))
+		else if (!strcmp("path",keyword))
 		{
-			return TRUE;
+			mPathParams.importLegacyStream(input_stream);
 		}
-		else if (*(a+2) > *(b+2))
+		else
 		{
-			return FALSE;
+			llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
 		}
-
-		return FALSE;
 	}
-};
 
-BOOL equalTriangle(const S32 *a, const S32 *b)
-{
-	if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
-	{
-		return TRUE;
-	}
-	return FALSE;
+	return TRUE;
 }
 
-BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
-									const std::vector<Point>& input_vertices,
-									const S32 num_input_triangles,
-									S32 *input_triangles,
-									S32 &num_output_vertices,
-									LLVector3 **output_vertices,
-									S32 &num_output_triangles,
-									S32 **output_triangles)
+BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
 {
 	LLMemType m1(LLMemType::MTYPE_VOLUME);
 	
-	/* Testing: avoid any cleanup
-	static BOOL skip_cleanup = TRUE;
-	if ( skip_cleanup )
-	{
-		num_output_vertices = num_input_vertices;
-		num_output_triangles = num_input_triangles;
+	output_stream <<"\tshape 0\n";
+	output_stream <<"\t{\n";
+	mPathParams.exportLegacyStream(output_stream);
+	mProfileParams.exportLegacyStream(output_stream);
+	output_stream << "\t}\n";
+	return TRUE;
+}
 
-		*output_vertices = new LLVector3[num_input_vertices];
-		for (S32 index = 0; index < num_input_vertices; index++)
-		{
-			(*output_vertices)[index] = input_vertices[index].mPos;
-		}
+LLSD LLVolumeParams::sculptAsLLSD() const
+{
+	LLSD sd = LLSD();
+	sd["id"] = getSculptID();
+	sd["type"] = getSculptType();
 
-		*output_triangles = new S32[num_input_triangles*3];
-		memcpy(*output_triangles, input_triangles, 3*num_input_triangles*sizeof(S32));		// Flawfinder: ignore
-		return TRUE;
-	}
-	*/
+	return sd;
+}
 
-	// Here's how we do this:
-	// Create a structure which contains the original vertex index and the
-	// LLVector3 data.
-	// "Sort" the data by the vectors
-	// Create an array the size of the old vertex list, with a mapping of
-	// old indices to new indices.
-	// Go through triangles, shift so the lowest index is first
-	// Sort triangles by first index
-	// Remove duplicate triangles
-	// Allocate and pack new triangle data.
+bool LLVolumeParams::sculptFromLLSD(LLSD& sd)
+{
+	setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger());
+	return true;
+}
 
-	//LLTimer cleanupTimer;
-	//llinfos << "In vertices: " << num_input_vertices << llendl;
-	//llinfos << "In triangles: " << num_input_triangles << llendl;
+LLSD LLVolumeParams::asLLSD() const
+{
+	LLSD sd = LLSD();
+	sd["path"] = mPathParams;
+	sd["profile"] = mProfileParams;
+	sd["sculpt"] = sculptAsLLSD();
+	
+	return sd;
+}
 
-	S32 i;
-	typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
-	vertex_set_t vertex_list;
+bool LLVolumeParams::fromLLSD(LLSD& sd)
+{
+	mPathParams.fromLLSD(sd["path"]);
+	mProfileParams.fromLLSD(sd["profile"]);
+	sculptFromLLSD(sd["sculpt"]);
+		
+	return true;
+}
 
-	LLVertexIndexPair *pairp = NULL;
-	for (i = 0; i < num_input_vertices; i++)
+void LLVolumeParams::reduceS(F32 begin, F32 end)
+{
+	begin = llclampf(begin);
+	end = llclampf(end);
+	if (begin > end)
 	{
-		LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
-		vertex_list.insert(new_pairp);
+		F32 temp = begin;
+		begin = end;
+		end = temp;
 	}
+	F32 a = mProfileParams.getBegin();
+	F32 b = mProfileParams.getEnd();
+	mProfileParams.setBegin(a + begin * (b - a));
+	mProfileParams.setEnd(a + end * (b - a));
+}
 
-	// Generate the vertex mapping and the list of vertices without
-	// duplicates.  This will crash if there are no vertices.
-	llassert(num_input_vertices > 0); // check for no vertices!
-	S32 *vertex_mapping = new S32[num_input_vertices];
-	LLVector3 *new_vertices = new LLVector3[num_input_vertices];
-	LLVertexIndexPair *prev_pairp = NULL;
+void LLVolumeParams::reduceT(F32 begin, F32 end)
+{
+	begin = llclampf(begin);
+	end = llclampf(end);
+	if (begin > end)
+	{
+		F32 temp = begin;
+		begin = end;
+		end = temp;
+	}
+	F32 a = mPathParams.getBegin();
+	F32 b = mPathParams.getEnd();
+	mPathParams.setBegin(a + begin * (b - a));
+	mPathParams.setEnd(a + end * (b - a));
+}
 
-	S32 new_num_vertices;
+const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f;	// 1/8 unity
+const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f;	// 1/9 unity
 
-	new_num_vertices = 0;
-	for (vertex_set_t::iterator iter = vertex_list.begin(),
-			 end = vertex_list.end();
-		 iter != end; iter++)
+// returns TRUE if the shape can be approximated with a convex shape 
+// for collison purposes
+BOOL LLVolumeParams::isConvex() const
+{
+	if (!getSculptID().isNull())
 	{
-		pairp = *iter;
-		if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))	
-		{
-			new_vertices[new_num_vertices] = pairp->mVertex;
-			//llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
-			new_num_vertices++;
-			// Update the previous
-			prev_pairp = pairp;
-		}
-		else
-		{
-			//llinfos << "Removed duplicate vertex " << pairp->mVertex << ", distance magVecSquared() is " << (pairp->mVertex - prev_pairp->mVertex).magVecSquared() << llendl;
-		}
-		vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
+		// can't determine, be safe and say no:
+		return FALSE;
+	}
+	
+	F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
+	F32 hollow = mProfileParams.getHollow();
+	 
+	U8 path_type = mPathParams.getCurveType();
+	if ( path_length > MIN_CONCAVE_PATH_WEDGE
+		&& ( mPathParams.getTwist() != mPathParams.getTwistBegin()
+		     || (hollow > 0.f 
+				 && LL_PCODE_PATH_LINE != path_type) ) )
+	{
+		// twist along a "not too short" path is concave
+		return FALSE;
 	}
 
-	// Iterate through triangles and remove degenerates, re-ordering vertices
-	// along the way.
-	S32 *new_triangles = new S32[num_input_triangles * 3];
-	S32 new_num_triangles = 0;
+	F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
+	BOOL same_hole = hollow == 0.f 
+					 || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
 
-	for (i = 0; i < num_input_triangles; i++)
+	F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
+	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
 	{
-		S32 v1 = i*3;
-		S32 v2 = v1 + 1;
-		S32 v3 = v1 + 2;
+		// it is a sphere and spheres get twice the minimum profile wedge
+		min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
+	}
 
-		//llinfos << "Checking triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
-		input_triangles[v1] = vertex_mapping[input_triangles[v1]];
-		input_triangles[v2] = vertex_mapping[input_triangles[v2]];
-		input_triangles[v3] = vertex_mapping[input_triangles[v3]];
+	BOOL convex_profile = ( ( profile_length == 1.f
+						     || profile_length <= 0.5f )
+						   && hollow == 0.f )						// trivially convex
+						  || ( profile_length <= min_profile_wedge
+							  && same_hole );						// effectvely convex (even when hollow)
 
-		if ((input_triangles[v1] == input_triangles[v2])
-			|| (input_triangles[v1] == input_triangles[v3])
-			|| (input_triangles[v2] == input_triangles[v3]))
-		{
-			//llinfos << "Removing degenerate triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
-			// Degenerate triangle, skip
-			continue;
-		}
+	if (!convex_profile)
+	{
+		// profile is concave
+		return FALSE;
+	}
 
-		if (input_triangles[v1] < input_triangles[v2])
-		{
-			if (input_triangles[v1] < input_triangles[v3])
-			{
-				// (0 < 1) && (0 < 2)
-				new_triangles[new_num_triangles*3] = input_triangles[v1];
-				new_triangles[new_num_triangles*3+1] = input_triangles[v2];
-				new_triangles[new_num_triangles*3+2] = input_triangles[v3];
-			}
-			else
-			{
-				// (0 < 1) && (2 < 0)
-				new_triangles[new_num_triangles*3] = input_triangles[v3];
-				new_triangles[new_num_triangles*3+1] = input_triangles[v1];
-				new_triangles[new_num_triangles*3+2] = input_triangles[v2];
-			}
-		}
-		else if (input_triangles[v2] < input_triangles[v3])
-		{
-			// (1 < 0) && (1 < 2)
-			new_triangles[new_num_triangles*3] = input_triangles[v2];
-			new_triangles[new_num_triangles*3+1] = input_triangles[v3];
-			new_triangles[new_num_triangles*3+2] = input_triangles[v1];
-		}
-		else
-		{
-			// (1 < 0) && (2 < 1)
-			new_triangles[new_num_triangles*3] = input_triangles[v3];
-			new_triangles[new_num_triangles*3+1] = input_triangles[v1];
-			new_triangles[new_num_triangles*3+2] = input_triangles[v2];
-		}
-		new_num_triangles++;
+	if ( LL_PCODE_PATH_LINE == path_type )
+	{
+		// straight paths with convex profile
+		return TRUE;
 	}
 
-	if (new_num_triangles == 0)
+	BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
+	if (concave_path)
 	{
-		llwarns << "Created volume object with 0 faces." << llendl;
-		delete[] new_triangles;
-		delete[] vertex_mapping;
-		delete[] new_vertices;
 		return FALSE;
 	}
 
-	typedef std::set<S32*, lessTriangle> triangle_set_t;
-	triangle_set_t triangle_list;
-
-	for (i = 0; i < new_num_triangles; i++)
+	// we're left with spheres, toroids and tubes
+	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+	{
+		// at this stage all spheres must be convex
+		return TRUE;
+	}
+
+	// it's a toroid or tube		
+	if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
 	{
-		triangle_list.insert(&new_triangles[i*3]);
+		// effectively convex
+		return TRUE;
 	}
 
-	// Sort through the triangle list, and delete duplicates
+	return FALSE;
+}
 
-	S32 *prevp = NULL;
-	S32 *curp = NULL;
+// debug
+void LLVolumeParams::setCube()
+{
+	mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
+	mProfileParams.setBegin(0.f);
+	mProfileParams.setEnd(1.f);
+	mProfileParams.setHollow(0.f);
 
-	S32 *sorted_tris = new S32[new_num_triangles*3];
-	S32 cur_tri = 0;
-	for (triangle_set_t::iterator iter = triangle_list.begin(),
-			 end = triangle_list.end();
-		 iter != end; iter++)
+	mPathParams.setBegin(0.f);
+	mPathParams.setEnd(1.f);
+	mPathParams.setScale(1.f, 1.f);
+	mPathParams.setShear(0.f, 0.f);
+	mPathParams.setCurveType(LL_PCODE_PATH_LINE);
+	mPathParams.setTwistBegin(0.f);
+	mPathParams.setTwistEnd(0.f);
+	mPathParams.setRadiusOffset(0.f);
+	mPathParams.setTaper(0.f, 0.f);
+	mPathParams.setRevolutions(0.f);
+	mPathParams.setSkew(0.f);
+}
+
+LLFaceID LLVolume::generateFaceMask()
+{
+	LLFaceID new_mask = 0x0000;
+
+	switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK)
 	{
-		curp = *iter;
-		if (!prevp || !equalTriangle(prevp, curp))
+	case LL_PCODE_PROFILE_CIRCLE:
+	case LL_PCODE_PROFILE_CIRCLE_HALF:
+		new_mask |= LL_FACE_OUTER_SIDE_0;
+		break;
+	case LL_PCODE_PROFILE_SQUARE:
 		{
-			//llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
-			sorted_tris[cur_tri*3] = *curp;
-			sorted_tris[cur_tri*3+1] = *(curp+1);
-			sorted_tris[cur_tri*3+2] = *(curp+2);
-			cur_tri++;
-			prevp = curp;
+			for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++)
+			{
+				new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+			}
 		}
-		else
+		break;
+	case LL_PCODE_PROFILE_ISOTRI:
+	case LL_PCODE_PROFILE_EQUALTRI:
+	case LL_PCODE_PROFILE_RIGHTTRI:
 		{
-			//llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+			for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++)
+			{
+				new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+			}
 		}
+		break;
+	default:
+		llerrs << "Unknown profile!" << llendl;
+		break;
 	}
 
-	*output_vertices = new LLVector3[new_num_vertices];
-	num_output_vertices = new_num_vertices;
-	for (i = 0; i < new_num_vertices; i++)
+	// handle hollow objects
+	if (mParams.getProfileParams().getHollow() > 0)
 	{
-		(*output_vertices)[i] = new_vertices[i];
+		new_mask |= LL_FACE_INNER_SIDE;
 	}
 
-	*output_triangles = new S32[cur_tri*3];
-	num_output_triangles = cur_tri;
-	memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32));		/* Flawfinder: ignore */
+	// handle open profile curves
+	if (mProfilep->isOpen())
+	{
+		new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
+	}
 
-	/*
-	llinfos << "Out vertices: " << num_output_vertices << llendl;
-	llinfos << "Out triangles: " << num_output_triangles << llendl;
-	for (i = 0; i < num_output_vertices; i++)
+	// handle open path curves
+	if (mPathp->isOpen())
 	{
-		llinfos << i << ":" << (*output_vertices)[i] << llendl;
+		new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
 	}
-	for (i = 0; i < num_output_triangles; i++)
+
+	return new_mask;
+}
+
+BOOL LLVolume::isFaceMaskValid(LLFaceID face_mask)
+{
+	LLFaceID test_mask = 0;
+	for(S32 i = 0; i < getNumFaces(); i++)
 	{
-		llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
+		test_mask |= mProfilep->mFaces[i].mFaceID;
 	}
-	*/
 
-	//llinfos << "Out vertices: " << num_output_vertices << llendl;
-	//llinfos << "Out triangles: " << num_output_triangles << llendl;
-	delete[] vertex_mapping;
-	vertex_mapping = NULL;
-	delete[] new_vertices;
-	new_vertices = NULL;
-	delete[] new_triangles;
-	new_triangles = NULL;
-	delete[] sorted_tris;
-	sorted_tris = NULL;
-	triangle_list.clear();
-	std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
-	vertex_list.clear();
-	
-	return TRUE;
+	return test_mask == face_mask;
+}
+
+BOOL LLVolume::isConvex() const
+{
+	// mParams.isConvex() may return FALSE even though the final
+	// geometry is actually convex due to LOD approximations.
+	// TODO -- provide LLPath and LLProfile with isConvex() methods
+	// that correctly determine convexity. -- Leviathan
+	return mParams.isConvex();
 }
 
 
-BOOL LLVolumeParams::importFile(LLFILE *fp)
+std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
 {
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	
-	//llinfos << "importing volume" << llendl;
-	const S32 BUFSIZE = 16384;
-	char buffer[BUFSIZE];	/* Flawfinder: ignore */
-	// *NOTE: changing the size or type of this buffer will require
-	// changing the sscanf below.
-	char keyword[256];	/* Flawfinder: ignore */
-	keyword[0] = 0;
+	s << "{type=" << (U32) profile_params.mCurveType;
+	s << ", begin=" << profile_params.mBegin;
+	s << ", end=" << profile_params.mEnd;
+	s << ", hollow=" << profile_params.mHollow;
+	s << "}";
+	return s;
+}
 
-	while (!feof(fp))
-	{
-		if (fgets(buffer, BUFSIZE, fp) == NULL)
-		{
-			buffer[0] = '\0';
-		}
-		
-		sscanf(buffer, " %255s", keyword);	/* Flawfinder: ignore */
-		if (!strcmp("{", keyword))
-		{
-			continue;
-		}
-		if (!strcmp("}",keyword))
-		{
-			break;
-		}
-		else if (!strcmp("profile", keyword))
-		{
-			mProfileParams.importFile(fp);
-		}
-		else if (!strcmp("path",keyword))
-		{
-			mPathParams.importFile(fp);
-		}
-		else
-		{
-			llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
-		}
-	}
 
-	return TRUE;
+std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
+{
+	s << "{type=" << (U32) path_params.mCurveType;
+	s << ", begin=" << path_params.mBegin;
+	s << ", end=" << path_params.mEnd;
+	s << ", twist=" << path_params.mTwistEnd;
+	s << ", scale=" << path_params.mScale;
+	s << ", shear=" << path_params.mShear;
+	s << ", twist_begin=" << path_params.mTwistBegin;
+	s << ", radius_offset=" << path_params.mRadiusOffset;
+	s << ", taper=" << path_params.mTaper;
+	s << ", revolutions=" << path_params.mRevolutions;
+	s << ", skew=" << path_params.mSkew;
+	s << "}";
+	return s;
 }
 
-BOOL LLVolumeParams::exportFile(LLFILE *fp) const
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
 {
-	fprintf(fp,"\tshape 0\n");
-	fprintf(fp,"\t{\n");
-	mPathParams.exportFile(fp);
-	mProfileParams.exportFile(fp);
-	fprintf(fp, "\t}\n");
-	return TRUE;
+	s << "{profileparams = " << volume_params.mProfileParams;
+	s << ", pathparams = " << volume_params.mPathParams;
+	s << "}";
+	return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
+{
+	s << " {open=" << (U32) profile.mOpen;
+	s << ", dirty=" << profile.mDirty;
+	s << ", totalout=" << profile.mTotalOut;
+	s << ", total=" << profile.mTotal;
+	s << "}";
+	return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPath &path)
+{
+	s << "{open=" << (U32) path.mOpen;
+	s << ", dirty=" << path.mDirty;
+	s << ", step=" << path.mStep;
+	s << ", total=" << path.mTotal;
+	s << "}";
+	return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
+{
+	s << "{params = " << volume.getParams();
+	s << ", path = " << *volume.mPathp;
+	s << ", profile = " << *volume.mProfilep;
+	s << "}";
+	return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
+{
+	s << "{params = " << volumep->getParams();
+	s << ", path = " << *(volumep->mPathp);
+	s << ", profile = " << *(volumep->mProfilep);
+	s << "}";
+	return s;
+}
+
+LLVolumeFace::LLVolumeFace() : 
+	mID(0),
+	mTypeMask(0),
+	mBeginS(0),
+	mBeginT(0),
+	mNumS(0),
+	mNumT(0),
+	mNumVertices(0),
+	mNumIndices(0),
+	mPositions(NULL),
+	mNormals(NULL),
+	mBinormals(NULL),
+	mTexCoords(NULL),
+	mIndices(NULL),
+	mWeights(NULL),
+	mOctree(NULL)
+{
+	mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
+	mCenter = mExtents+2;
 }
 
+LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
+:	mID(0),
+	mTypeMask(0),
+	mBeginS(0),
+	mBeginT(0),
+	mNumS(0),
+	mNumT(0),
+	mNumVertices(0),
+	mNumIndices(0),
+	mPositions(NULL),
+	mNormals(NULL),
+	mBinormals(NULL),
+	mTexCoords(NULL),
+	mIndices(NULL),
+	mWeights(NULL),
+	mOctree(NULL)
+{ 
+	mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
+	mCenter = mExtents+2;
+	*this = src;
+}
 
-BOOL LLVolumeParams::importLegacyStream(std::istream& input_stream)
+LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
 {
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	if (&src == this)
+	{ //self assignment, do nothing
+		return *this;
+	}
+
+	mID = src.mID;
+	mTypeMask = src.mTypeMask;
+	mBeginS = src.mBeginS;
+	mBeginT = src.mBeginT;
+	mNumS = src.mNumS;
+	mNumT = src.mNumT;
+
+	mExtents[0] = src.mExtents[0];
+	mExtents[1] = src.mExtents[1];
+	*mCenter = *src.mCenter;
+
+	mNumVertices = 0;
+	mNumIndices = 0;
+
+	freeData();
 	
-	//llinfos << "importing volume" << llendl;
-	const S32 BUFSIZE = 16384;
-	// *NOTE: changing the size or type of this buffer will require
-	// changing the sscanf below.
-	char buffer[BUFSIZE];		/* Flawfinder: ignore */
-	char keyword[256];		/* Flawfinder: ignore */
-	keyword[0] = 0;
+	LLVector4a::memcpyNonAliased16((F32*) mExtents, (F32*) src.mExtents, 3*sizeof(LLVector4a));
 
-	while (input_stream.good())
+	resizeVertices(src.mNumVertices);
+	resizeIndices(src.mNumIndices);
+
+	if (mNumVertices)
 	{
-		input_stream.getline(buffer, BUFSIZE);
-		sscanf(buffer, " %255s", keyword);
-		if (!strcmp("{", keyword))
-		{
-			continue;
-		}
-		if (!strcmp("}",keyword))
+		S32 vert_size = mNumVertices*sizeof(LLVector4a);
+		S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF;
+			
+		LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size);
+		LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size);
+		LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size);
+
+
+		if (src.mBinormals)
 		{
-			break;
+			allocateBinormals(src.mNumVertices);
+			LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) src.mBinormals, vert_size);
 		}
-		else if (!strcmp("profile", keyword))
+		else
 		{
-			mProfileParams.importLegacyStream(input_stream);
+			ll_aligned_free_16(mBinormals);
+			mBinormals = NULL;
 		}
-		else if (!strcmp("path",keyword))
+
+		if (src.mWeights)
 		{
-			mPathParams.importLegacyStream(input_stream);
+			allocateWeights(src.mNumVertices);
+			LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size);
 		}
 		else
 		{
-			llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+			ll_aligned_free_16(mWeights);
+			mWeights = NULL;
 		}
 	}
 
-	return TRUE;
+	if (mNumIndices)
+	{
+		S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF;
+		
+		LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size);
+	}
+	
+	//delete 
+	return *this;
 }
 
-BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
+LLVolumeFace::~LLVolumeFace()
 {
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	
-	output_stream <<"\tshape 0\n";
-	output_stream <<"\t{\n";
-	mPathParams.exportLegacyStream(output_stream);
-	mProfileParams.exportLegacyStream(output_stream);
-	output_stream << "\t}\n";
-	return TRUE;
+	ll_aligned_free_16(mExtents);
+	mExtents = NULL;
+
+	freeData();
 }
 
-LLSD LLVolumeParams::asLLSD() const
+void LLVolumeFace::freeData()
 {
-	LLSD sd = LLSD();
-	sd["path"] = mPathParams;
-	sd["profile"] = mProfileParams;
-	return sd;
+	ll_aligned_free_16(mPositions);
+	mPositions = NULL;
+	ll_aligned_free_16( mNormals);
+	mNormals = NULL;
+	ll_aligned_free_16(mTexCoords);
+	mTexCoords = NULL;
+	ll_aligned_free_16(mIndices);
+	mIndices = NULL;
+	ll_aligned_free_16(mBinormals);
+	mBinormals = NULL;
+	ll_aligned_free_16(mWeights);
+	mWeights = NULL;
+
+	delete mOctree;
+	mOctree = NULL;
 }
 
-bool LLVolumeParams::fromLLSD(LLSD& sd)
+BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
 {
-	mPathParams.fromLLSD(sd["path"]);
-	mProfileParams.fromLLSD(sd["profile"]);
-	return true;
+	//tree for this face is no longer valid
+	delete mOctree;
+	mOctree = NULL;
+
+	BOOL ret = FALSE ;
+	if (mTypeMask & CAP_MASK)
+	{
+		ret = createCap(volume, partial_build);
+	}
+	else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
+	{
+		ret = createSide(volume, partial_build);
+	}
+	else
+	{
+		llerrs << "Unknown/uninitialized face type!" << llendl;
+	}
+
+	//update the range of the texture coordinates
+	if(ret)
+	{
+		mTexCoordExtents[0].setVec(1.f, 1.f) ;
+		mTexCoordExtents[1].setVec(0.f, 0.f) ;
+
+		for(U32 i = 0 ; i < mNumVertices ; i++)
+		{
+			if(mTexCoordExtents[0].mV[0] > mTexCoords[i].mV[0])
+			{
+				mTexCoordExtents[0].mV[0] = mTexCoords[i].mV[0] ;
+			}
+			if(mTexCoordExtents[1].mV[0] < mTexCoords[i].mV[0])
+			{
+				mTexCoordExtents[1].mV[0] = mTexCoords[i].mV[0] ;
+			}
+
+			if(mTexCoordExtents[0].mV[1] > mTexCoords[i].mV[1])
+			{
+				mTexCoordExtents[0].mV[1] = mTexCoords[i].mV[1] ;
+			}
+			if(mTexCoordExtents[1].mV[1] < mTexCoords[i].mV[1])
+			{
+				mTexCoordExtents[1].mV[1] = mTexCoords[i].mV[1] ;
+			}			
+		}
+		mTexCoordExtents[0].mV[0] = llmax(0.f, mTexCoordExtents[0].mV[0]) ;
+		mTexCoordExtents[0].mV[1] = llmax(0.f, mTexCoordExtents[0].mV[1]) ;
+		mTexCoordExtents[1].mV[0] = llmin(1.f, mTexCoordExtents[1].mV[0]) ;
+		mTexCoordExtents[1].mV[1] = llmin(1.f, mTexCoordExtents[1].mV[1]) ;
+	}
+
+	return ret ;
 }
 
-void LLVolumeParams::reduceS(F32 begin, F32 end)
+void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv)
 {
-	begin = llclampf(begin);
-	end = llclampf(end);
-	if (begin > end)
+	cv.setPosition(mPositions[index]);
+	cv.setNormal(mNormals[index]);
+	cv.mTexCoord = mTexCoords[index];
+}
+
+bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const
+{
+	return getPosition().equals3(rhs.getPosition()) &&
+		mTexCoord == rhs.mTexCoord &&
+		getNormal().equals3(rhs.getNormal());
+}
+
+bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const
+{
+	if (a.mV[0] != b.mV[0])
 	{
-		F32 temp = begin;
-		begin = end;
-		end = temp;
+		return a.mV[0] < b.mV[0];
 	}
-	F32 a = mProfileParams.getBegin();
-	F32 b = mProfileParams.getEnd();
-	mProfileParams.setBegin(a + begin * (b - a));
-	mProfileParams.setEnd(a + end * (b - a));
+	
+	if (a.mV[1] != b.mV[1])
+	{
+		return a.mV[1] < b.mV[1];
+	}
+	
+	return a.mV[2] < b.mV[2];
 }
 
-void LLVolumeParams::reduceT(F32 begin, F32 end)
+void LLVolumeFace::optimize(F32 angle_cutoff)
 {
-	begin = llclampf(begin);
-	end = llclampf(end);
-	if (begin > end)
+	LLVolumeFace new_face;
+
+	//map of points to vector of vertices at that point
+	VertexMapData::PointMap point_map;
+
+	//remove redundant vertices
+	for (U32 i = 0; i < mNumIndices; ++i)
 	{
-		F32 temp = begin;
-		begin = end;
-		end = temp;
+		U16 index = mIndices[i];
+
+		LLVolumeFace::VertexData cv;
+		getVertexData(index, cv);
+		
+		BOOL found = FALSE;
+		VertexMapData::PointMap::iterator point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
+		if (point_iter != point_map.end())
+		{ //duplicate point might exist
+			for (U32 j = 0; j < point_iter->second.size(); ++j)
+			{
+				LLVolumeFace::VertexData& tv = (point_iter->second)[j];
+				if (tv.compareNormal(cv, angle_cutoff))
+				{
+					found = TRUE;
+					new_face.pushIndex((point_iter->second)[j].mIndex);
+					break;
+				}
+			}
+		}
+
+		if (!found)
+		{
+			new_face.pushVertex(cv);
+			U16 index = (U16) new_face.mNumVertices-1;
+			new_face.pushIndex(index);
+
+			VertexMapData d;
+			d.setPosition(cv.getPosition());
+			d.mTexCoord = cv.mTexCoord;
+			d.setNormal(cv.getNormal());
+			d.mIndex = index;
+			if (point_iter != point_map.end())
+			{
+				point_iter->second.push_back(d);
+			}
+			else
+			{
+				point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
+			}
+		}
 	}
-	F32 a = mPathParams.getBegin();
-	F32 b = mPathParams.getEnd();
-	mPathParams.setBegin(a + begin * (b - a));
-	mPathParams.setEnd(a + end * (b - a));
+
+	swapData(new_face);
 }
 
-const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f;	// 1/8 unity
-const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f;	// 1/9 unity
+class LLVCacheTriangleData;
 
-// returns TRUE if the shape can be approximated with a convex shape 
-// for collison purposes
-BOOL LLVolumeParams::isConvex() const
+class LLVCacheVertexData
 {
-	F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
-	F32 hollow = mProfileParams.getHollow();
-	 
-	U8 path_type = mPathParams.getCurveType();
-	if ( path_length > MIN_CONCAVE_PATH_WEDGE
-		&& ( mPathParams.getTwist() != mPathParams.getTwistBegin()
-		     || (hollow > 0.f 
-				 && LL_PCODE_PATH_LINE != path_type) ) )
+public:
+	S32 mIdx;
+	S32 mCacheTag;
+	F32 mScore;
+	U32 mActiveTriangles;
+	std::vector<LLVCacheTriangleData*> mTriangles;
+
+	LLVCacheVertexData()
 	{
-		// twist along a "not too short" path is concave
-		return FALSE;
+		mCacheTag = -1;
+		mScore = 0.f;
+		mActiveTriangles = 0;
+		mIdx = -1;
 	}
+};
 
-	F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
-	BOOL same_hole = hollow == 0.f 
-					 || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
+class LLVCacheTriangleData
+{
+public:
+	bool mActive;
+	F32 mScore;
+	LLVCacheVertexData* mVertex[3];
 
-	F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
-	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
-	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+	LLVCacheTriangleData()
 	{
-		// it is a sphere and spheres get twice the minimum profile wedge
-		min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
+		mActive = true;
+		mScore = 0.f;
+		mVertex[0] = mVertex[1] = mVertex[2] = NULL;
 	}
 
-	BOOL convex_profile = ( ( profile_length == 1.f
-						     || profile_length <= 0.5f )
-						   && hollow == 0.f )						// trivially convex
-						  || ( profile_length <= min_profile_wedge
-							  && same_hole );						// effectvely convex (even when hollow)
-
-	if (!convex_profile)
+	void complete()
 	{
-		// profile is concave
-		return FALSE;
+		mActive = false;
+		for (S32 i = 0; i < 3; ++i)
+		{
+			if (mVertex[i])
+			{
+				llassert_always(mVertex[i]->mActiveTriangles > 0);
+				mVertex[i]->mActiveTriangles--;
+			}
+		}
 	}
 
-	if ( LL_PCODE_PATH_LINE == path_type )
-	{
-		// straight paths with convex profile
-		return TRUE;
+	bool operator<(const LLVCacheTriangleData& rhs) const
+	{ //highest score first
+		return rhs.mScore < mScore;
 	}
+};
 
-	BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
-	if (concave_path)
-	{
-		return FALSE;
+const F32 FindVertexScore_CacheDecayPower = 1.5f;
+const F32 FindVertexScore_LastTriScore = 0.75f;
+const F32 FindVertexScore_ValenceBoostScale = 2.0f;
+const F32 FindVertexScore_ValenceBoostPower = 0.5f;
+const U32 MaxSizeVertexCache = 32;
+
+F32 find_vertex_score(LLVCacheVertexData& data)
+{
+	if (data.mActiveTriangles == 0)
+	{ //no triangle references this vertex
+		return -1.f;
 	}
 
-	// we're left with spheres, toroids and tubes
-	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+	F32 score = 0.f;
+
+	S32 cache_idx = data.mCacheTag;
+
+	if (cache_idx < 0)
 	{
-		// at this stage all spheres must be convex
-		return TRUE;
+		//not in cache
 	}
-
-	// it's a toroid or tube		
-	if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
+	else
 	{
-		// effectively convex
-		return TRUE;
+		if (cache_idx < 3)
+		{ //vertex was in the last triangle
+			score = FindVertexScore_LastTriScore;
+		}
+		else
+		{ //more points for being higher in the cache
+			F32 scaler = 1.f/(MaxSizeVertexCache-3);
+			score = 1.f-((cache_idx-3)*scaler);
+			score = powf(score, FindVertexScore_CacheDecayPower);
+		}
 	}
 
-	return FALSE;
+	//bonus points for having low valence
+	F32 valence_boost = powf(data.mActiveTriangles, -FindVertexScore_ValenceBoostPower);
+	score += FindVertexScore_ValenceBoostScale * valence_boost;
+
+	return score;
 }
 
-// debug
-void LLVolumeParams::setCube()
+class LLVCacheFIFO
 {
-	mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
-	mProfileParams.setBegin(0.f);
-	mProfileParams.setEnd(1.f);
-	mProfileParams.setHollow(0.f);
+public:
+	LLVCacheVertexData* mCache[MaxSizeVertexCache];
+	U32 mMisses;
 
-	mPathParams.setBegin(0.f);
-	mPathParams.setEnd(1.f);
-	mPathParams.setScale(1.f, 1.f);
-	mPathParams.setShear(0.f, 0.f);
-	mPathParams.setCurveType(LL_PCODE_PATH_LINE);
-	mPathParams.setTwistBegin(0.f);
-	mPathParams.setTwistEnd(0.f);
-	mPathParams.setRadiusOffset(0.f);
-	mPathParams.setTaper(0.f, 0.f);
-	mPathParams.setRevolutions(0.f);
-	mPathParams.setSkew(0.f);
-}
+	LLVCacheFIFO()
+	{
+		mMisses = 0;
+		for (U32 i = 0; i < MaxSizeVertexCache; ++i)
+		{
+			mCache[i] = NULL;
+		}
+	}
+
+	void addVertex(LLVCacheVertexData* data)
+	{
+		if (data->mCacheTag == -1)
+		{
+			mMisses++;
+
+			S32 end = MaxSizeVertexCache-1;
+
+			if (mCache[end])
+			{
+				mCache[end]->mCacheTag = -1;
+			}
+
+			for (S32 i = end; i > 0; --i)
+			{
+				mCache[i] = mCache[i-1];
+				if (mCache[i])
+				{
+					mCache[i]->mCacheTag = i;
+				}
+			}
+
+			mCache[0] = data;
+			data->mCacheTag = 0;
+		}
+	}
+};
 
-LLFaceID LLVolume::generateFaceMask()
+class LLVCacheLRU
 {
-	LLFaceID new_mask = 0x0000;
+public:
+	LLVCacheVertexData* mCache[MaxSizeVertexCache+3];
 
-	switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK)
+	LLVCacheTriangleData* mBestTriangle;
+	
+	U32 mMisses;
+
+	LLVCacheLRU()
 	{
-	case LL_PCODE_PROFILE_CIRCLE:
-	case LL_PCODE_PROFILE_CIRCLE_HALF:
-		new_mask |= LL_FACE_OUTER_SIDE_0;
-		break;
-	case LL_PCODE_PROFILE_SQUARE:
+		for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
 		{
-			for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++)
-			{
-				new_mask |= LL_FACE_OUTER_SIDE_0 << side;
-			}
+			mCache[i] = NULL;
 		}
-		break;
-	case LL_PCODE_PROFILE_ISOTRI:
-	case LL_PCODE_PROFILE_EQUALTRI:
-	case LL_PCODE_PROFILE_RIGHTTRI:
+
+		mBestTriangle = NULL;
+		mMisses = 0;
+	}
+
+	void addVertex(LLVCacheVertexData* data)
+	{
+		S32 end = MaxSizeVertexCache+2;
+		if (data->mCacheTag != -1)
+		{ //just moving a vertex to the front of the cache
+			end = data->mCacheTag;
+		}
+		else
 		{
-			for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++)
+			mMisses++;
+			if (mCache[end])
+			{ //adding a new vertex, vertex at end of cache falls off
+				mCache[end]->mCacheTag = -1;
+			}
+		}
+
+		for (S32 i = end; i > 0; --i)
+		{ //adjust cache pointers and tags
+			mCache[i] = mCache[i-1];
+
+			if (mCache[i])
 			{
-				new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+				mCache[i]->mCacheTag = i;			
 			}
 		}
-		break;
-	default:
-		llerrs << "Unknown profile!" << llendl;
-		break;
-	}
 
-	// handle hollow objects
-	if (mParams.getProfileParams().getHollow() > 0)
-	{
-		new_mask |= LL_FACE_INNER_SIDE;
+		mCache[0] = data;
+		mCache[0]->mCacheTag = 0;
 	}
 
-	// handle open profile curves
-	if (mProfilep->isOpen())
+	void addTriangle(LLVCacheTriangleData* data)
 	{
-		new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
+		addVertex(data->mVertex[0]);
+		addVertex(data->mVertex[1]);
+		addVertex(data->mVertex[2]);
 	}
 
-	// handle open path curves
-	if (mPathp->isOpen())
+	void updateScores()
 	{
-		new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
-	}
+		for (U32 i = MaxSizeVertexCache; i < MaxSizeVertexCache+3; ++i)
+		{ //trailing 3 vertices aren't actually in the cache for scoring purposes
+			if (mCache[i])
+			{
+				mCache[i]->mCacheTag = -1;
+			}
+		}
 
-	return new_mask;
-}
+		for (U32 i = 0; i < MaxSizeVertexCache; ++i)
+		{ //update scores of vertices in cache
+			if (mCache[i])
+			{
+				mCache[i]->mScore = find_vertex_score(*(mCache[i]));
+				llassert_always(mCache[i]->mCacheTag == i);
+			}
+		}
 
-BOOL LLVolume::isFaceMaskValid(LLFaceID face_mask)
-{
-	LLFaceID test_mask = 0;
-	for(S32 i = 0; i < getNumFaces(); i++)
-	{
-		test_mask |= mProfilep->mFaces[i].mFaceID;
+		mBestTriangle = NULL;
+		//update triangle scores
+		for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
+		{
+			if (mCache[i])
+			{
+				for (U32 j = 0; j < mCache[i]->mTriangles.size(); ++j)
+				{
+					LLVCacheTriangleData* tri = mCache[i]->mTriangles[j];
+					if (tri->mActive)
+					{
+						tri->mScore = tri->mVertex[0]->mScore;
+						tri->mScore += tri->mVertex[1]->mScore;
+						tri->mScore += tri->mVertex[2]->mScore;
+
+						if (!mBestTriangle || mBestTriangle->mScore < tri->mScore)
+						{
+							mBestTriangle = tri;
+						}
+					}
+				}
+			}
+		}
+
+		//knock trailing 3 vertices off the cache
+		for (U32 i = MaxSizeVertexCache; i < MaxSizeVertexCache+3; ++i)
+		{
+			if (mCache[i])
+			{
+				llassert_always(mCache[i]->mCacheTag == -1);
+				mCache[i] = NULL;
+			}
+		}
 	}
+};
 
-	return test_mask == face_mask;
-}
 
-BOOL LLVolume::isConvex() const
-{
-	// mParams.isConvex() may return FALSE even though the final
-	// geometry is actually convex due to LOD approximations.
-	// TODO -- provide LLPath and LLProfile with isConvex() methods
-	// that correctly determine convexity. -- Leviathan
-	return mParams.isConvex();
-}
+void LLVolumeFace::cacheOptimize()
+{ //optimize for vertex cache according to Forsyth method: 
+  // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
+	
+	LLVCacheLRU cache;
+	
+	//mapping of vertices to triangles and indices
+	std::vector<LLVCacheVertexData> vertex_data;
 
+	//mapping of triangles do vertices
+	std::vector<LLVCacheTriangleData> triangle_data;
 
-std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
-{
-	s << "{type=" << (U32) profile_params.mCurveType;
-	s << ", begin=" << profile_params.mBegin;
-	s << ", end=" << profile_params.mEnd;
-	s << ", hollow=" << profile_params.mHollow;
-	s << "}";
-	return s;
-}
+	triangle_data.resize(mNumIndices/3);
+	vertex_data.resize(mNumVertices);
 
+	for (U32 i = 0; i < mNumIndices; i++)
+	{ //populate vertex data and triangle data arrays
+		U16 idx = mIndices[i];
+		U32 tri_idx = i/3;
 
-std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
-{
-	s << "{type=" << (U32) path_params.mCurveType;
-	s << ", begin=" << path_params.mBegin;
-	s << ", end=" << path_params.mEnd;
-	s << ", twist=" << path_params.mTwistEnd;
-	s << ", scale=" << path_params.mScale;
-	s << ", shear=" << path_params.mShear;
-	s << ", twist_begin=" << path_params.mTwistBegin;
-	s << ", radius_offset=" << path_params.mRadiusOffset;
-	s << ", taper=" << path_params.mTaper;
-	s << ", revolutions=" << path_params.mRevolutions;
-	s << ", skew=" << path_params.mSkew;
-	s << "}";
-	return s;
-}
+		vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx]));
+		vertex_data[idx].mIdx = idx;
+		triangle_data[tri_idx].mVertex[i%3] = &(vertex_data[idx]);
+	}
 
+	/*F32 pre_acmr = 1.f;
+	//measure cache misses from before rebuild
+	{
+		LLVCacheFIFO test_cache;
+		for (U32 i = 0; i < mNumIndices; ++i)
+		{
+			test_cache.addVertex(&vertex_data[mIndices[i]]);
+		}
 
-std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
-{
-	s << "{profileparams = " << volume_params.mProfileParams;
-	s << ", pathparams = " << volume_params.mPathParams;
-	s << "}";
-	return s;
-}
+		for (U32 i = 0; i < mNumVertices; i++)
+		{
+			vertex_data[i].mCacheTag = -1;
+		}
 
+		pre_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
+	}*/
 
-std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
-{
-	s << " {open=" << (U32) profile.mOpen;
-	s << ", dirty=" << profile.mDirty;
-	s << ", totalout=" << profile.mTotalOut;
-	s << ", total=" << profile.mTotal;
-	s << "}";
-	return s;
-}
+	for (U32 i = 0; i < mNumVertices; i++)
+	{ //initialize score values (no cache -- might try a fifo cache here)
+		vertex_data[i].mScore = find_vertex_score(vertex_data[i]);
+		vertex_data[i].mActiveTriangles = vertex_data[i].mTriangles.size();
 
+		for (U32 j = 0; j < vertex_data[i].mTriangles.size(); ++j)
+		{
+			vertex_data[i].mTriangles[j]->mScore += vertex_data[i].mScore;
+		}
+	}
 
-std::ostream& operator<<(std::ostream &s, const LLPath &path)
-{
-	s << "{open=" << (U32) path.mOpen;
-	s << ", dirty=" << path.mDirty;
-	s << ", step=" << path.mStep;
-	s << ", total=" << path.mTotal;
-	s << "}";
-	return s;
-}
+	//sort triangle data by score
+	std::sort(triangle_data.begin(), triangle_data.end());
 
-std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
-{
-	s << "{params = " << volume.getParams();
-	s << ", path = " << *volume.mPathp;
-	s << ", profile = " << *volume.mProfilep;
-	s << "}";
-	return s;
-}
+	std::vector<U16> new_indices;
 
+	LLVCacheTriangleData* tri;
 
-std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
-{
-	s << "{params = " << volumep->getParams();
-	s << ", path = " << *(volumep->mPathp);
-	s << ", profile = " << *(volumep->mProfilep);
-	s << "}";
-	return s;
-}
+	//prime pump by adding first triangle to cache;
+	tri = &(triangle_data[0]);
+	cache.addTriangle(tri);
+	new_indices.push_back(tri->mVertex[0]->mIdx);
+	new_indices.push_back(tri->mVertex[1]->mIdx);
+	new_indices.push_back(tri->mVertex[2]->mIdx);
+	tri->complete();
 
+	U32 breaks = 0;
+	for (U32 i = 1; i < mNumIndices/3; ++i)
+	{
+		cache.updateScores();
+		tri = cache.mBestTriangle;
+		if (!tri)
+		{
+			breaks++;
+			for (U32 j = 0; j < triangle_data.size(); ++j)
+			{
+				if (triangle_data[j].mActive)
+				{
+					tri = &(triangle_data[j]);
+					break;
+				}
+			}
+		}	
+		
+		cache.addTriangle(tri);
+		new_indices.push_back(tri->mVertex[0]->mIdx);
+		new_indices.push_back(tri->mVertex[1]->mIdx);
+		new_indices.push_back(tri->mVertex[2]->mIdx);
+		tri->complete();
+	}
 
-BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
-{
-	BOOL ret = FALSE ;
-	if (mTypeMask & CAP_MASK)
+	for (U32 i = 0; i < mNumIndices; ++i)
 	{
-		ret = createCap(volume, partial_build);
+		mIndices[i] = new_indices[i];
 	}
-	else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
+
+	/*F32 post_acmr = 1.f;
+	//measure cache misses from after rebuild
 	{
-		ret = createSide(volume, partial_build);
+		LLVCacheFIFO test_cache;
+		for (U32 i = 0; i < mNumVertices; i++)
+		{
+			vertex_data[i].mCacheTag = -1;
+		}
+
+		for (U32 i = 0; i < mNumIndices; ++i)
+		{
+			test_cache.addVertex(&vertex_data[mIndices[i]]);
+		}
+		
+		post_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
+	}*/
+
+	//optimize for pre-TnL cache
+	
+	//allocate space for new buffer
+	S32 num_verts = mNumVertices;
+	LLVector4a* pos = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+	LLVector4a* norm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+	S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
+	LLVector2* tc = (LLVector2*) ll_aligned_malloc_16(size);
+
+	LLVector4a* wght = NULL;
+	if (mWeights)
+	{
+		wght = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
 	}
-	else
+
+	LLVector4a* binorm = NULL;
+	if (mBinormals)
 	{
-		llerrs << "Unknown/uninitialized face type!" << llendl;
+		binorm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
 	}
 
-	//update the range of the texture coordinates
-	if(ret)
+	//allocate mapping of old indices to new indices
+	std::vector<S32> new_idx;
+	new_idx.resize(mNumVertices, -1);
+
+	S32 cur_idx = 0;
+	for (U32 i = 0; i < mNumIndices; ++i)
 	{
-		mTexCoordExtents[0].setVec(1.f, 1.f) ;
-		mTexCoordExtents[1].setVec(0.f, 0.f) ;
+		U16 idx = mIndices[i];
+		if (new_idx[idx] == -1)
+		{ //this vertex hasn't been added yet
+			new_idx[idx] = cur_idx;
 
-		U32 end = mVertices.size() ;
-		for(U32 i = 0 ; i < end ; i++)
-		{
-			if(mTexCoordExtents[0].mV[0] > mVertices[i].mTexCoord.mV[0])
+			//copy vertex data
+			pos[cur_idx] = mPositions[idx];
+			norm[cur_idx] = mNormals[idx];
+			tc[cur_idx] = mTexCoords[idx];
+			if (mWeights)
 			{
-				mTexCoordExtents[0].mV[0] = mVertices[i].mTexCoord.mV[0] ;
+				wght[cur_idx] = mWeights[idx];
 			}
-			if(mTexCoordExtents[1].mV[0] < mVertices[i].mTexCoord.mV[0])
+			if (mBinormals)
 			{
-				mTexCoordExtents[1].mV[0] = mVertices[i].mTexCoord.mV[0] ;
+				binorm[cur_idx] = mBinormals[idx];
 			}
 
-			if(mTexCoordExtents[0].mV[1] > mVertices[i].mTexCoord.mV[1])
-			{
-				mTexCoordExtents[0].mV[1] = mVertices[i].mTexCoord.mV[1] ;
-			}
-			if(mTexCoordExtents[1].mV[1] < mVertices[i].mTexCoord.mV[1])
-			{
-				mTexCoordExtents[1].mV[1] = mVertices[i].mTexCoord.mV[1] ;
-			}			
+			cur_idx++;
 		}
-		mTexCoordExtents[0].mV[0] = llmax(0.f, mTexCoordExtents[0].mV[0]) ;
-		mTexCoordExtents[0].mV[1] = llmax(0.f, mTexCoordExtents[0].mV[1]) ;
-		mTexCoordExtents[1].mV[0] = llmin(1.f, mTexCoordExtents[1].mV[0]) ;
-		mTexCoordExtents[1].mV[1] = llmin(1.f, mTexCoordExtents[1].mV[1]) ;
 	}
 
-	return ret ;
+	for (U32 i = 0; i < mNumIndices; ++i)
+	{
+		mIndices[i] = new_idx[mIndices[i]];
+	}
+	
+	ll_aligned_free_16(mPositions);
+	ll_aligned_free_16(mNormals);
+	ll_aligned_free_16(mTexCoords);
+	ll_aligned_free_16(mWeights);
+	ll_aligned_free_16(mBinormals);
+
+	mPositions = pos;
+	mNormals = norm;
+	mTexCoords = tc;
+	mWeights = wght;
+	mBinormals = binorm;
+
+	//std::string result = llformat("ACMR pre/post: %.3f/%.3f  --  %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
+	//llinfos << result << llendl;
+
+}
+
+void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size)
+{
+	if (mOctree)
+	{
+		return;
+	}
+
+	mOctree = new LLOctreeRoot<LLVolumeTriangle>(center, size, NULL);
+	new LLVolumeOctreeListener(mOctree);
+
+	for (U32 i = 0; i < mNumIndices; i+= 3)
+	{ //for each triangle
+		LLPointer<LLVolumeTriangle> tri = new LLVolumeTriangle();
+				
+		const LLVector4a& v0 = mPositions[mIndices[i]];
+		const LLVector4a& v1 = mPositions[mIndices[i+1]];
+		const LLVector4a& v2 = mPositions[mIndices[i+2]];
+
+		//store pointers to vertex data
+		tri->mV[0] = &v0;
+		tri->mV[1] = &v1;
+		tri->mV[2] = &v2;
+
+		//store indices
+		tri->mIndex[0] = mIndices[i];
+		tri->mIndex[1] = mIndices[i+1];
+		tri->mIndex[2] = mIndices[i+2];
+
+		//get minimum point
+		LLVector4a min = v0;
+		min.setMin(min, v1);
+		min.setMin(min, v2);
+
+		//get maximum point
+		LLVector4a max = v0;
+		max.setMax(max, v1);
+		max.setMax(max, v2);
+
+		//compute center
+		LLVector4a center;
+		center.setAdd(min, max);
+		center.mul(0.5f);
+
+		tri->mPositionGroup = center;
+
+		//compute "radius"
+		LLVector4a size;
+		size.setSub(max,min);
+		
+		tri->mRadius = size.getLength3().getF32() * scaler;
+		
+		//insert
+		mOctree->insert(tri);
+	}
+
+	//remove unneeded octree layers
+	while (!mOctree->balance())	{ }
+
+	//calculate AABB for each node
+	LLVolumeOctreeRebound rebound(this);
+	rebound.traverse(mOctree);
+
+	if (gDebugGL)
+	{
+		LLVolumeOctreeValidate validate;
+		validate.traverse(mOctree);
+	}
+}
+
+
+void LLVolumeFace::swapData(LLVolumeFace& rhs)
+{
+	llswap(rhs.mPositions, mPositions);
+	llswap(rhs.mNormals, mNormals);
+	llswap(rhs.mBinormals, mBinormals);
+	llswap(rhs.mTexCoords, mTexCoords);
+	llswap(rhs.mIndices,mIndices);
+	llswap(rhs.mNumVertices, mNumVertices);
+	llswap(rhs.mNumIndices, mNumIndices);
 }
 
 void	LerpPlanarVertex(LLVolumeFace::VertexData& v0,
@@ -4463,10 +6010,21 @@ void	LerpPlanarVertex(LLVolumeFace::VertexData& v0,
 				   F32	coef01,
 				   F32	coef02)
 {
-	vout.mPosition = v0.mPosition + ((v1.mPosition-v0.mPosition)*coef01)+((v2.mPosition-v0.mPosition)*coef02);
+
+	LLVector4a lhs;
+	lhs.setSub(v1.getPosition(), v0.getPosition());
+	lhs.mul(coef01);
+	LLVector4a rhs;
+	rhs.setSub(v2.getPosition(), v0.getPosition());
+	rhs.mul(coef02);
+
+	rhs.add(lhs);
+	rhs.add(v0.getPosition());
+
+	vout.setPosition(rhs);
+		
 	vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
-	vout.mNormal = v0.mNormal;
-	vout.mBinormal = v0.mBinormal;
+	vout.setNormal(v0.getNormal());
 }
 
 BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
@@ -4486,84 +6044,113 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
 	num_vertices = (grid_size+1)*(grid_size+1);
 	num_indices = quad_count * 4;
 
-	LLVector3& min = mExtents[0];
-	LLVector3& max = mExtents[1];
+	LLVector4a& min = mExtents[0];
+	LLVector4a& max = mExtents[1];
 
 	S32 offset = 0;
 	if (mTypeMask & TOP_MASK)
+	{
 		offset = (max_t-1) * max_s;
+	}
 	else
+	{
 		offset = mBeginS;
+	}
+
+	{
+		VertexData	corners[4];
+		VertexData baseVert;
+		for(S32 t = 0; t < 4; t++)
+		{
+			corners[t].getPosition().load3( mesh[offset + (grid_size*t)].mPos.mV);
+			corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
+			corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
+		}
+
+		{
+			LLVector4a lhs;
+			lhs.setSub(corners[1].getPosition(), corners[0].getPosition());
+			LLVector4a rhs;
+			rhs.setSub(corners[2].getPosition(), corners[1].getPosition());
+			baseVert.getNormal().setCross3(lhs, rhs); 
+			baseVert.getNormal().normalize3fast();
+		}
+
+		if(!(mTypeMask & TOP_MASK))
+		{
+			baseVert.getNormal().mul(-1.0f);
+		}
+		else
+		{
+			//Swap the UVs on the U(X) axis for top face
+			LLVector2 swap;
+			swap = corners[0].mTexCoord;
+			corners[0].mTexCoord=corners[3].mTexCoord;
+			corners[3].mTexCoord=swap;
+			swap = corners[1].mTexCoord;
+			corners[1].mTexCoord=corners[2].mTexCoord;
+			corners[2].mTexCoord=swap;
+		}
 
-	VertexData	corners[4];
-	VertexData baseVert;
-	for(int t = 0; t < 4; t++){
-		corners[t].mPosition = mesh[offset + (grid_size*t)].mPos;
-		corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
-		corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
-	}
-	baseVert.mNormal = 
-		((corners[1].mPosition-corners[0].mPosition) % 
-		(corners[2].mPosition-corners[1].mPosition));
-	baseVert.mNormal.normVec();
-	if(!(mTypeMask & TOP_MASK)){
-		baseVert.mNormal *= -1.0f;
-	}else{
-		//Swap the UVs on the U(X) axis for top face
-		LLVector2 swap;
-		swap = corners[0].mTexCoord;
-		corners[0].mTexCoord=corners[3].mTexCoord;
-		corners[3].mTexCoord=swap;
-		swap = corners[1].mTexCoord;
-		corners[1].mTexCoord=corners[2].mTexCoord;
-		corners[2].mTexCoord=swap;
-	}
-	baseVert.mBinormal = calc_binormal_from_triangle( 
-		corners[0].mPosition, corners[0].mTexCoord,
-		corners[1].mPosition, corners[1].mTexCoord,
-		corners[2].mPosition, corners[2].mTexCoord);
-	for(int t = 0; t < 4; t++){
-		corners[t].mBinormal = baseVert.mBinormal;
-		corners[t].mNormal = baseVert.mNormal;
-	}
-	mHasBinormals = TRUE;
+		LLVector4a binormal;
+		
+		calc_binormal_from_triangle( binormal,
+			corners[0].getPosition(), corners[0].mTexCoord,
+			corners[1].getPosition(), corners[1].mTexCoord,
+			corners[2].getPosition(), corners[2].mTexCoord);
+		
+		binormal.normalize3fast();
 
-	if (partial_build)
-	{
-		mVertices.clear();
-	}
+		S32 size = (grid_size+1)*(grid_size+1);
+		resizeVertices(size);
+		allocateBinormals(size);
 
-	S32	vtop = mVertices.size();
-	for(int gx = 0;gx<grid_size+1;gx++){
-		for(int gy = 0;gy<grid_size+1;gy++){
-			VertexData newVert;
-			LerpPlanarVertex(
-				corners[0],
-				corners[1],
-				corners[3],
-				newVert,
-				(F32)gx/(F32)grid_size,
-				(F32)gy/(F32)grid_size);
-			mVertices.push_back(newVert);
+		LLVector4a* pos = (LLVector4a*) mPositions;
+		LLVector4a* norm = (LLVector4a*) mNormals;
+		LLVector4a* binorm = (LLVector4a*) mBinormals;
+		LLVector2* tc = (LLVector2*) mTexCoords;
 
-			if (gx == 0 && gy == 0)
-			{
-				min = max = newVert.mPosition;
-			}
-			else
+		for(int gx = 0;gx<grid_size+1;gx++)
+		{
+			for(int gy = 0;gy<grid_size+1;gy++)
 			{
-				update_min_max(min,max,newVert.mPosition);
+				VertexData newVert;
+				LerpPlanarVertex(
+					corners[0],
+					corners[1],
+					corners[3],
+					newVert,
+					(F32)gx/(F32)grid_size,
+					(F32)gy/(F32)grid_size);
+
+				*pos++ = newVert.getPosition();
+				*norm++ = baseVert.getNormal();
+				*tc++ = newVert.mTexCoord;
+				*binorm++ = binormal;
+
+				if (gx == 0 && gy == 0)
+				{
+					min = newVert.getPosition();
+					max = min;
+				}
+				else
+				{
+					min.setMin(min, newVert.getPosition());
+					max.setMax(max, newVert.getPosition());
+				}
 			}
 		}
-	}
 	
-	mCenter = (min + max) * 0.5f;
+		mCenter->setAdd(min, max);
+		mCenter->mul(0.5f); 
+	}
 
 	if (!partial_build)
 	{
-#if GEN_TRI_STRIP
-		mTriStrip.clear();
-#endif
+		resizeIndices(grid_size*grid_size*6);
+
+		U16* out = mIndices;
+
 		S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
 		for(S32 gx = 0;gx<grid_size;gx++)
 		{
@@ -4574,61 +6161,18 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
 				{
 					for(S32 i=5;i>=0;i--)
 					{
-						mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
-					}
-					
-#if GEN_TRI_STRIP
-					if (gy == 0)
-					{
-						mTriStrip.push_back((gx+1)*(grid_size+1));
-						mTriStrip.push_back((gx+1)*(grid_size+1));
-						mTriStrip.push_back(gx*(grid_size+1));
-					}
-
-					mTriStrip.push_back(gy+1+(gx+1)*(grid_size+1));
-					mTriStrip.push_back(gy+1+gx*(grid_size+1));
-					
-					
-					if (gy == grid_size-1)
-					{
-						mTriStrip.push_back(gy+1+gx*(grid_size+1));
-					}
-#endif
+						*out++ = ((gy*(grid_size+1))+gx+idxs[i]);
+					}		
 				}
 				else
 				{
 					for(S32 i=0;i<6;i++)
 					{
-						mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
-					}
-
-#if GEN_TRI_STRIP
-					if (gy == 0)
-					{
-						mTriStrip.push_back(gx*(grid_size+1));
-						mTriStrip.push_back(gx*(grid_size+1));
-						mTriStrip.push_back((gx+1)*(grid_size+1));
-					}
-
-					mTriStrip.push_back(gy+1+gx*(grid_size+1));
-					mTriStrip.push_back(gy+1+(gx+1)*(grid_size+1));
-					
-					if (gy == grid_size-1)
-					{
-						mTriStrip.push_back(gy+1+(gx+1)*(grid_size+1));
+						*out++ = ((gy*(grid_size+1))+gx+idxs[i]);
 					}
-#endif
 				}
-			}
-			
-		}
-
-#if GEN_TRI_STRIP
-		if (mTriStrip.size()%2 == 1)
-		{
-			mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
+			}	
 		}
-#endif
 	}
 		
 	return TRUE;
@@ -4658,17 +6202,31 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	num_vertices = profile.size();
 	num_indices = (profile.size() - 2)*3;
 
-	mVertices.resize(num_vertices);
+	if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+	{
+		resizeVertices(num_vertices+1);
+		allocateBinormals(num_vertices+1);	
 
-	if (!partial_build)
+		if (!partial_build)
+		{
+			resizeIndices(num_indices+3);
+		}
+	}
+	else
 	{
-		mIndices.resize(num_indices);
+		resizeVertices(num_vertices);
+		allocateBinormals(num_vertices);
+
+		if (!partial_build)
+		{
+			resizeIndices(num_indices);
+		}
 	}
 
 	S32 max_s = volume->getProfile().getTotal();
 	S32 max_t = volume->getPath().mPath.size();
 
-	mCenter.clearVec();
+	mCenter->clear();
 
 	S32 offset = 0;
 	if (mTypeMask & TOP_MASK)
@@ -4686,82 +6244,91 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	LLVector2 cuv;
 	LLVector2 min_uv, max_uv;
 
-	LLVector3& min = mExtents[0];
-	LLVector3& max = mExtents[1];
+	LLVector4a& min = mExtents[0];
+	LLVector4a& max = mExtents[1];
+
+	LLVector2* tc = (LLVector2*) mTexCoords;
+	LLVector4a* pos = (LLVector4a*) mPositions;
+	LLVector4a* norm = (LLVector4a*) mNormals;
+	LLVector4a* binorm = (LLVector4a*) mBinormals;
 
 	// Copy the vertices into the array
 	for (S32 i = 0; i < num_vertices; i++)
 	{
 		if (mTypeMask & TOP_MASK)
 		{
-			mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
-			mVertices[i].mTexCoord.mV[1] = profile[i].mV[1]+0.5f;
+			tc[i].mV[0] = profile[i].mV[0]+0.5f;
+			tc[i].mV[1] = profile[i].mV[1]+0.5f;
 		}
 		else
 		{
 			// Mirror for underside.
-			mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
-			mVertices[i].mTexCoord.mV[1] = 0.5f - profile[i].mV[1];
+			tc[i].mV[0] = profile[i].mV[0]+0.5f;
+			tc[i].mV[1] = 0.5f - profile[i].mV[1];
 		}
 
-		mVertices[i].mPosition = mesh[i + offset].mPos;
+		pos[i].load3(mesh[i + offset].mPos.mV);
 		
 		if (i == 0)
 		{
-			min = max = mVertices[i].mPosition;
-			min_uv = max_uv = mVertices[i].mTexCoord;
+			max = pos[i];
+			min = max;
+			min_uv = max_uv = tc[i];
 		}
 		else
 		{
-			update_min_max(min,max, mVertices[i].mPosition);
-			update_min_max(min_uv, max_uv, mVertices[i].mTexCoord);
+			update_min_max(min,max,pos[i]);
+			update_min_max(min_uv, max_uv, tc[i]);
 		}
 	}
 
-	mCenter = (min+max)*0.5f;
+	mCenter->setAdd(min, max);
+	mCenter->mul(0.5f); 
+
 	cuv = (min_uv + max_uv)*0.5f;
 
-	LLVector3 binormal = calc_binormal_from_triangle( 
-		mCenter, cuv,
-		mVertices[0].mPosition, mVertices[0].mTexCoord,
-		mVertices[1].mPosition, mVertices[1].mTexCoord);
-	binormal.normVec();
+	LLVector4a binormal;
+	calc_binormal_from_triangle(binormal,
+		*mCenter, cuv,
+		pos[0], tc[0],
+		pos[1], tc[1]);
+	binormal.normalize3fast();
+
+	LLVector4a normal;
+	LLVector4a d0, d1;
+	
 
-	LLVector3 d0;
-	LLVector3 d1;
-	LLVector3 normal;
+	d0.setSub(*mCenter, pos[0]);
+	d1.setSub(*mCenter, pos[1]);
 
-	d0 = mCenter-mVertices[0].mPosition;
-	d1 = mCenter-mVertices[1].mPosition;
+	if (mTypeMask & TOP_MASK)
+	{
+		normal.setCross3(d0, d1);
+	}
+	else
+	{
+		normal.setCross3(d1, d0);
+	}
 
-	normal = (mTypeMask & TOP_MASK) ? (d0%d1) : (d1%d0);
-	normal.normVec();
+	normal.normalize3fast();
 
 	VertexData vd;
-	vd.mPosition = mCenter;
-	vd.mNormal = normal;
-	vd.mBinormal = binormal;
+	vd.setPosition(*mCenter);
 	vd.mTexCoord = cuv;
 	
 	if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
 	{
-		mVertices.push_back(vd);
+		pos[num_vertices] = *mCenter;
+		tc[num_vertices] = cuv;
 		num_vertices++;
-		if (!partial_build)
-		{
-			vector_append(mIndices, 3);
-		}
 	}
 		
-	
 	for (S32 i = 0; i < num_vertices; i++)
 	{
-		mVertices[i].mBinormal = binormal;
-		mVertices[i].mNormal = normal;
+		binorm[i].load4a(binormal.getF32ptr());
+		norm[i].load4a(normal.getF32ptr());
 	}
 
-	mHasBinormals = TRUE;
-
 	if (partial_build)
 	{
 		return TRUE;
@@ -4869,8 +6436,6 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 					pt2--;
 				}
 			}
-
-			makeTriStrip();
 		}
 		else
 		{
@@ -4975,8 +6540,6 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 					pt2--;
 				}
 			}
-
-			makeTriStrip();
 		}
 	}
 	else
@@ -4998,131 +6561,277 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 			mIndices[3*i+v2] = i + 1;
 		}
 
-#if GEN_TRI_STRIP
-		//make tri strip
-		if (mTypeMask & OPEN_MASK)
-		{
-			makeTriStrip();
-		}
-		else
-		{
-			S32 j = num_vertices-2;
-			if (mTypeMask & TOP_MASK)
-			{
-				mTriStrip.push_back(0);
-				for (S32 i = 0; i <= j; ++i)
-				{
-					mTriStrip.push_back(i);
-					if (i != j)
-					{
-						mTriStrip.push_back(j);
-					}
-					--j;
-				}
-			}
-			else
-			{
-				mTriStrip.push_back(j);
-				for (S32 i = 0; i <= j; ++i)
-				{
-					if (i != j)
-					{
-						mTriStrip.push_back(j);
-					}
-					mTriStrip.push_back(i);
-					--j;
-				}
-			}
-			
-			mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
 
-			if (mTriStrip.size()%2 == 1)
-			{
-				mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
-			}
-		}
-#endif
 	}
 		
 	return TRUE;
 }
 
-void LLVolumeFace::makeTriStrip()
+void LLVolumeFace::createBinormals()
 {
-#if GEN_TRI_STRIP
-	for (U32 i = 0; i < mIndices.size(); i+=3)
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	if (!mBinormals)
 	{
-		U16 i0 = mIndices[i];
-		U16 i1 = mIndices[i+1];
-		U16 i2 = mIndices[i+2];
+		allocateBinormals(mNumVertices);
 
-		if ((i/3)%2 == 1)
-		{
-			mTriStrip.push_back(i0);
-			mTriStrip.push_back(i0);
-			mTriStrip.push_back(i1);
-			mTriStrip.push_back(i2);
-			mTriStrip.push_back(i2);
-		}
-		else
+		//generate binormals
+		LLVector4a* pos = mPositions;
+		LLVector2* tc = (LLVector2*) mTexCoords;
+		LLVector4a* binorm = (LLVector4a*) mBinormals;
+
+		LLVector4a* end = mBinormals+mNumVertices;
+		while (binorm < end)
 		{
-			mTriStrip.push_back(i2);
-			mTriStrip.push_back(i2);
-			mTriStrip.push_back(i1);
-			mTriStrip.push_back(i0);
-			mTriStrip.push_back(i0);
+			(*binorm++).clear();
 		}
-	}
 
-	if (mTriStrip.size()%2 == 1)
-	{
-		mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
-	}
-#endif
-}
+		binorm = mBinormals;
 
-void LLVolumeFace::createBinormals()
-{
-	LLMemType m1(LLMemType::MTYPE_VOLUME);
-	
-	if (!mHasBinormals)
-	{
-		//generate binormals
-		for (U32 i = 0; i < mIndices.size()/3; i++) 
+		for (U32 i = 0; i < mNumIndices/3; i++) 
 		{	//for each triangle
-			const VertexData& v0 = mVertices[mIndices[i*3+0]];
-			const VertexData& v1 = mVertices[mIndices[i*3+1]];
-			const VertexData& v2 = mVertices[mIndices[i*3+2]];
+			const U16& i0 = mIndices[i*3+0];
+			const U16& i1 = mIndices[i*3+1];
+			const U16& i2 = mIndices[i*3+2];
 						
 			//calculate binormal
-			LLVector3 binorm = calc_binormal_from_triangle(v0.mPosition, v0.mTexCoord,
-															v1.mPosition, v1.mTexCoord,
-															v2.mPosition, v2.mTexCoord);
+			LLVector4a binormal;
+			calc_binormal_from_triangle(binormal,
+										pos[i0], tc[i0],
+										pos[i1], tc[i1],
+										pos[i2], tc[i2]);
 
-			for (U32 j = 0; j < 3; j++) 
-			{ //add triangle normal to vertices
-				mVertices[mIndices[i*3+j]].mBinormal += binorm; // * (weight_sum - d[j])/weight_sum;
-			}
+
+			//add triangle normal to vertices
+			binorm[i0].add(binormal);
+			binorm[i1].add(binormal);
+			binorm[i2].add(binormal);
 
 			//even out quad contributions
 			if (i % 2 == 0) 
 			{
-				mVertices[mIndices[i*3+2]].mBinormal += binorm;
+				binorm[i2].add(binormal);
 			}
 			else 
 			{
-				mVertices[mIndices[i*3+1]].mBinormal += binorm;
+				binorm[i1].add(binormal);
 			}
 		}
 
 		//normalize binormals
-		for (U32 i = 0; i < mVertices.size(); i++) 
+		for (U32 i = 0; i < mNumVertices; i++) 
+		{
+			binorm[i].normalize3fast();
+			//bump map/planar projection code requires normals to be normalized
+			mNormals[i].normalize3fast();
+		}
+	}
+}
+
+void LLVolumeFace::resizeVertices(S32 num_verts)
+{
+	ll_aligned_free_16(mPositions);
+	ll_aligned_free_16(mNormals);
+	ll_aligned_free_16(mBinormals);
+	ll_aligned_free_16(mTexCoords);
+
+	mBinormals = NULL;
+
+	if (num_verts)
+	{
+		mPositions = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+		assert_aligned(mPositions, 16);
+		mNormals = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+		assert_aligned(mNormals, 16);
+
+		//pad texture coordinate block end to allow for QWORD reads
+		S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
+		mTexCoords = (LLVector2*) ll_aligned_malloc_16(size);
+		assert_aligned(mTexCoords, 16);
+	}
+	else
+	{
+		mPositions = NULL;
+		mNormals = NULL;
+		mTexCoords = NULL;
+	}
+
+	mNumVertices = num_verts;
+}
+
+void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv)
+{
+	pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord);
+}
+
+void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc)
+{
+	S32 new_verts = mNumVertices+1;
+	S32 new_size = new_verts*16;
+//	S32 old_size = mNumVertices*16;
+
+	//positions
+	mPositions = (LLVector4a*) realloc(mPositions, new_size);
+	
+	//normals
+	mNormals = (LLVector4a*) realloc(mNormals, new_size);
+	
+	//tex coords
+	new_size = ((new_verts*8)+0xF) & ~0xF;
+	mTexCoords = (LLVector2*) realloc(mTexCoords, new_size);
+	
+
+	//just clear binormals
+	ll_aligned_free_16(mBinormals);
+	mBinormals = NULL;
+
+	mPositions[mNumVertices] = pos;
+	mNormals[mNumVertices] = norm;
+	mTexCoords[mNumVertices] = tc;
+
+	mNumVertices++;	
+}
+
+void LLVolumeFace::allocateBinormals(S32 num_verts)
+{
+	ll_aligned_free_16(mBinormals);
+	mBinormals = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+}
+
+void LLVolumeFace::allocateWeights(S32 num_verts)
+{
+	ll_aligned_free_16(mWeights);
+	mWeights = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+}
+
+void LLVolumeFace::resizeIndices(S32 num_indices)
+{
+	ll_aligned_free_16(mIndices);
+	
+	if (num_indices)
+	{
+		//pad index block end to allow for QWORD reads
+		S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF;
+		
+		mIndices = (U16*) ll_aligned_malloc_16(size);
+	}
+	else
+	{
+		mIndices = NULL;
+	}
+
+	mNumIndices = num_indices;
+}
+
+void LLVolumeFace::pushIndex(const U16& idx)
+{
+	S32 new_count = mNumIndices + 1;
+	S32 new_size = ((new_count*2)+0xF) & ~0xF;
+
+	S32 old_size = ((mNumIndices*2)+0xF) & ~0xF;
+	if (new_size != old_size)
+	{
+		mIndices = (U16*) realloc(mIndices, new_size);
+	}
+	
+	mIndices[mNumIndices++] = idx;
+}
+
+void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx)
+{
+	resizeVertices(v.size());
+	resizeIndices(idx.size());
+
+	for (U32 i = 0; i < v.size(); ++i)
+	{
+		mPositions[i] = v[i].getPosition();
+		mNormals[i] = v[i].getNormal();
+		mTexCoords[i] = v[i].mTexCoord;
+	}
+
+	for (U32 i = 0; i < idx.size(); ++i)
+	{
+		mIndices[i] = idx[i];
+	}
+}
+
+void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMatrix4& norm_mat_in)
+{
+	U16 offset = mNumVertices;
+
+	S32 new_count = face.mNumVertices + mNumVertices;
+
+	if (new_count > 65536)
+	{
+		llerrs << "Cannot append face -- 16-bit overflow will occur." << llendl;
+	}
+	
+	if (face.mNumVertices == 0)
+	{
+		llerrs << "Cannot append empty face." << llendl;
+	}
+
+	//allocate new buffer space
+	mPositions = (LLVector4a*) realloc(mPositions, new_count*sizeof(LLVector4a));
+	assert_aligned(mPositions, 16);
+	mNormals = (LLVector4a*) realloc(mNormals, new_count*sizeof(LLVector4a));
+	assert_aligned(mNormals, 16);
+	mTexCoords = (LLVector2*) realloc(mTexCoords, (new_count*sizeof(LLVector2)+0xF) & ~0xF);
+	assert_aligned(mTexCoords, 16);
+	
+	mNumVertices = new_count;
+
+	//get destination address of appended face
+	LLVector4a* dst_pos = mPositions+offset;
+	LLVector2* dst_tc = mTexCoords+offset;
+	LLVector4a* dst_norm = mNormals+offset;
+
+	//get source addresses of appended face
+	const LLVector4a* src_pos = face.mPositions;
+	const LLVector2* src_tc = face.mTexCoords;
+	const LLVector4a* src_norm = face.mNormals;
+
+	//load aligned matrices
+	LLMatrix4a mat, norm_mat;
+	mat.loadu(mat_in);
+	norm_mat.loadu(norm_mat_in);
+
+	for (U32 i = 0; i < face.mNumVertices; ++i)
+	{
+		//transform appended face position and store
+		mat.affineTransform(src_pos[i], dst_pos[i]);
+
+		//transform appended face normal and store
+		norm_mat.rotate(src_norm[i], dst_norm[i]);
+		dst_norm[i].normalize3fast();
+
+		//copy appended face texture coordinate
+		dst_tc[i] = src_tc[i];
+
+		if (offset == 0 && i == 0)
+		{ //initialize bounding box
+			mExtents[0] = mExtents[1] = dst_pos[i];
+		}
+		else
 		{
-			mVertices[i].mBinormal.normVec();
-			mVertices[i].mNormal.normVec();
+			//stretch bounding box
+			update_min_max(mExtents[0], mExtents[1], dst_pos[i]);
 		}
+	}
+
 
-		mHasBinormals = TRUE;
+	new_count = mNumIndices + face.mNumIndices;
+
+	//allocate new index buffer
+	mIndices = (U16*) realloc(mIndices, (new_count*sizeof(U16)+0xF) & ~0xF);
+	
+	//get destination address into new index buffer
+	U16* dst_idx = mIndices+mNumIndices;
+	mNumIndices = new_count;
+
+	for (U32 i = 0; i < face.mNumIndices; ++i)
+	{ //copy indices, offsetting by old vertex count
+		dst_idx[i] = face.mIndices[i]+offset;
 	}
 }
 
@@ -5152,18 +6861,20 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 	num_vertices = mNumS*mNumT;
 	num_indices = (mNumS-1)*(mNumT-1)*6;
 
-	mVertices.resize(num_vertices);
-
 	if (!partial_build)
 	{
-		mIndices.resize(num_indices);
-		mEdge.resize(num_indices);
-	}
-	else
-	{
-		mHasBinormals = FALSE;
+		resizeVertices(num_vertices);
+		resizeIndices(num_indices);
+
+		if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
+		{
+			mEdge.resize(num_indices);
+		}
 	}
 
+	LLVector4a* pos = (LLVector4a*) mPositions;
+	LLVector4a* norm = (LLVector4a*) mNormals;
+	LLVector2* tc = (LLVector2*) mTexCoords;
 	S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
 	S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
 
@@ -5214,21 +6925,20 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 				i = mBeginS + s + max_s*t;
 			}
 
-			mVertices[cur_vertex].mPosition = mesh[i].mPos;
-			mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+			pos[cur_vertex].load3(mesh[i].mPos.mV);
+			tc[cur_vertex] = LLVector2(ss,tt);
 		
-			mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
-			mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
-
+			norm[cur_vertex].clear();
 			cur_vertex++;
 
 			if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
 			{
-				mVertices[cur_vertex].mPosition = mesh[i].mPos;
-				mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+				pos[cur_vertex].load3(mesh[i].mPos.mV);
+				tc[cur_vertex] = LLVector2(ss,tt);
 			
-				mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
-				mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+				norm[cur_vertex].clear();
+				
 				cur_vertex++;
 			}
 		}
@@ -5246,29 +6956,29 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 
 			i = mBeginS + s + max_s*t;
 			ss = profile[mBeginS + s].mV[2] - begin_stex;
-			mVertices[cur_vertex].mPosition = mesh[i].mPos;
-			mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
-		
-			mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
-			mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
-
+			pos[cur_vertex].load3(mesh[i].mPos.mV);
+			tc[cur_vertex] = LLVector2(ss,tt);
+			norm[cur_vertex].clear(); 
+			
 			cur_vertex++;
 		}
 	}
 	
 
 	//get bounding box for this side
-	LLVector3& face_min = mExtents[0];
-	LLVector3& face_max = mExtents[1];
-	mCenter.clearVec();
+	LLVector4a& face_min = mExtents[0];
+	LLVector4a& face_max = mExtents[1];
+	mCenter->clear();
 
-	face_min = face_max = mVertices[0].mPosition;
-	for (U32 i = 1; i < mVertices.size(); ++i)
+	face_min = face_max = pos[0];
+
+	for (U32 i = 1; i < mNumVertices; ++i)
 	{
-		update_min_max(face_min, face_max, mVertices[i].mPosition);
+		update_min_max(face_min, face_max, pos[i]);
 	}
 
-	mCenter = (face_min + face_max) * 0.5f;
+	mCenter->setAdd(face_min, face_max);
+	mCenter->mul(0.5f);
 
 	S32 cur_index = 0;
 	S32 cur_edge = 0;
@@ -5276,18 +6986,9 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 
 	if (!partial_build)
 	{
-#if GEN_TRI_STRIP
-		mTriStrip.clear();
-#endif
-
 		// Now we generate the indices.
 		for (t = 0; t < (mNumT-1); t++)
 		{
-#if GEN_TRI_STRIP
-			//prepend terminating index to strip
-			mTriStrip.push_back(mNumS*t);
-#endif
-
 			for (s = 0; s < (mNumS-1); s++)
 			{	
 				mIndices[cur_index++] = s   + mNumS*t;			//bottom left
@@ -5297,16 +6998,6 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 				mIndices[cur_index++] = s+1 + mNumS*t;			//bottom right
 				mIndices[cur_index++] = s+1 + mNumS*(t+1);		//top right
 
-#if GEN_TRI_STRIP
-				if (s == 0)
-				{
-					mTriStrip.push_back(s+mNumS*t);
-					mTriStrip.push_back(s+mNumS*(t+1));
-				}
-				mTriStrip.push_back(s+1+mNumS*t);
-				mTriStrip.push_back(s+1+mNumS*(t+1));
-#endif
-				
 				mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1;						//bottom left/top right neighbor face 
 				if (t < mNumT-2) {												//top right/top left neighbor face 
 					mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
@@ -5347,52 +7038,61 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 				}
 				mEdge[cur_edge++] = (mNumS-1)*2*t+s*2;							//top right/bottom left neighbor face	
 			}
-#if GEN_TRI_STRIP
-			//append terminating vertex to strip
-			mTriStrip.push_back(mNumS-1+mNumS*(t+1));
-#endif
 		}
+	}
 
-#if GEN_TRI_STRIP
-		if (mTriStrip.size()%2 == 1)
-		{
-			mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
-		}
-#endif
+	//clear normals
+	for (U32 i = 0; i < mNumVertices; i++)
+	{
+		mNormals[i].clear();
 	}
 
 	//generate normals 
-	for (U32 i = 0; i < mIndices.size()/3; i++) //for each triangle
+	for (U32 i = 0; i < mNumIndices/3; i++) //for each triangle
 	{
 		const U16* idx = &(mIndices[i*3]);
-			
-		VertexData* v[] = 
-		{	&mVertices[idx[0]], &mVertices[idx[1]], &mVertices[idx[2]] };
-					
-		//calculate triangle normal
-		LLVector3 norm = (v[0]->mPosition-v[1]->mPosition) % (v[0]->mPosition-v[2]->mPosition);
+		
 
-		v[0]->mNormal += norm;
-		v[1]->mNormal += norm;
-		v[2]->mNormal += norm;
+		LLVector4a* v[] = 
+		{	pos+idx[0], pos+idx[1], pos+idx[2] };
+		
+		LLVector4a* n[] = 
+		{	norm+idx[0], norm+idx[1], norm+idx[2] };
+		
+		//calculate triangle normal
+		LLVector4a a, b, c;
+		
+		a.setSub(*v[0], *v[1]);
+		b.setSub(*v[0], *v[2]);
+		c.setCross3(a,b);
 
+		n[0]->add(c);
+		n[1]->add(c);
+		n[2]->add(c);
+		
 		//even out quad contributions
-		v[i%2+1]->mNormal += norm;
+		n[i%2+1]->add(c);
 	}
 	
 	// adjust normals based on wrapping and stitching
 	
-	BOOL s_bottom_converges = ((mVertices[0].mPosition - mVertices[mNumS*(mNumT-2)].mPosition).magVecSquared() < 0.000001f);
-	BOOL s_top_converges = ((mVertices[mNumS-1].mPosition - mVertices[mNumS*(mNumT-2)+mNumS-1].mPosition).magVecSquared() < 0.000001f);
+	LLVector4a top;
+	top.setSub(pos[0], pos[mNumS*(mNumT-2)]);
+	BOOL s_bottom_converges = (top.dot3(top) < 0.000001f);
+
+	top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]);
+	BOOL s_top_converges = (top.dot3(top) < 0.000001f);
+
 	if (sculpt_stitching == LL_SCULPT_TYPE_NONE)  // logic for non-sculpt volumes
 	{
 		if (volume->getPath().isOpen() == FALSE)
 		{ //wrap normals on T
 			for (S32 i = 0; i < mNumS; i++)
 			{
-				LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal;
-				mVertices[i].mNormal = norm;
-				mVertices[mNumS*(mNumT-1)+i].mNormal = norm;
+				LLVector4a n;
+				n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
+				norm[i] = n;
+				norm[mNumS*(mNumT-1)+i] = n;
 			}
 		}
 
@@ -5400,9 +7100,10 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 		{ //wrap normals on S
 			for (S32 i = 0; i < mNumT; i++)
 			{
-				LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal;
-				mVertices[mNumS * i].mNormal = norm;
-				mVertices[mNumS * i+mNumS-1].mNormal = norm;
+				LLVector4a n;
+				n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
+				norm[mNumS * i] = n;
+				norm[mNumS * i+mNumS-1] = n;
 			}
 		}
 	
@@ -5413,7 +7114,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 			{ //all lower S have same normal
 				for (S32 i = 0; i < mNumT; i++)
 				{
-					mVertices[mNumS*i].mNormal = LLVector3(1,0,0);
+					norm[mNumS*i].set(1,0,0);
 				}
 			}
 
@@ -5421,12 +7122,11 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 			{ //all upper S have same normal
 				for (S32 i = 0; i < mNumT; i++)
 				{
-					mVertices[mNumS*i+mNumS-1].mNormal = LLVector3(-1,0,0);
+					norm[mNumS*i+mNumS-1].set(-1,0,0);
 				}
 			}
 		}
 	}
-	
 	else  // logic for sculpt volumes
 	{
 		BOOL average_poles = FALSE;
@@ -5449,30 +7149,33 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 		{
 			// average normals for north pole
 		
-			LLVector3 average(0.0, 0.0, 0.0);
+			LLVector4a average;
+			average.clear();
+
 			for (S32 i = 0; i < mNumS; i++)
 			{
-				average += mVertices[i].mNormal;
+				average.add(norm[i]);
 			}
 
 			// set average
 			for (S32 i = 0; i < mNumS; i++)
 			{
-				mVertices[i].mNormal = average;
+				norm[i] = average;
 			}
 
 			// average normals for south pole
 		
-			average = LLVector3(0.0, 0.0, 0.0);
+			average.clear();
+
 			for (S32 i = 0; i < mNumS; i++)
 			{
-				average += mVertices[i + mNumS * (mNumT - 1)].mNormal;
+				average.add(norm[i + mNumS * (mNumT - 1)]);
 			}
 
 			// set average
 			for (S32 i = 0; i < mNumS; i++)
 			{
-				mVertices[i + mNumS * (mNumT - 1)].mNormal = average;
+				norm[i + mNumS * (mNumT - 1)] = average;
 			}
 
 		}
@@ -5482,23 +7185,22 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 		{
 			for (S32 i = 0; i < mNumT; i++)
 			{
-				LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal;
-				mVertices[mNumS * i].mNormal = norm;
-				mVertices[mNumS * i+mNumS-1].mNormal = norm;
+				LLVector4a n;
+				n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
+				norm[mNumS * i] = n;
+				norm[mNumS * i+mNumS-1] = n;
 			}
 		}
 
-
-		
 		if (wrap_t)
 		{
 			for (S32 i = 0; i < mNumS; i++)
 			{
-				LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal;
-				mVertices[i].mNormal = norm;
-				mVertices[mNumS*(mNumT-1)+i].mNormal = norm;
+				LLVector4a n;
+				n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
+				norm[i] = n;
+				norm[mNumS*(mNumT-1)+i] = n;
 			}
-			
 		}
 
 	}
@@ -5508,41 +7210,51 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 
 // Finds binormal based on three vertices with texture coordinates.
 // Fills in dummy values if the triangle has degenerate texture coordinates.
-LLVector3 calc_binormal_from_triangle( 
-	const LLVector3& pos0,
+void calc_binormal_from_triangle(LLVector4a& binormal,
+
+	const LLVector4a& pos0,
 	const LLVector2& tex0,
-	const LLVector3& pos1,
+	const LLVector4a& pos1,
 	const LLVector2& tex1,
-	const LLVector3& pos2,
+	const LLVector4a& pos2,
 	const LLVector2& tex2)
 {
-	LLVector3 rx0( pos0.mV[VX], tex0.mV[VX], tex0.mV[VY] );
-	LLVector3 rx1( pos1.mV[VX], tex1.mV[VX], tex1.mV[VY] );
-	LLVector3 rx2( pos2.mV[VX], tex2.mV[VX], tex2.mV[VY] );
+	LLVector4a rx0( pos0[VX], tex0.mV[VX], tex0.mV[VY] );
+	LLVector4a rx1( pos1[VX], tex1.mV[VX], tex1.mV[VY] );
+	LLVector4a rx2( pos2[VX], tex2.mV[VX], tex2.mV[VY] );
 	
-	LLVector3 ry0( pos0.mV[VY], tex0.mV[VX], tex0.mV[VY] );
-	LLVector3 ry1( pos1.mV[VY], tex1.mV[VX], tex1.mV[VY] );
-	LLVector3 ry2( pos2.mV[VY], tex2.mV[VX], tex2.mV[VY] );
+	LLVector4a ry0( pos0[VY], tex0.mV[VX], tex0.mV[VY] );
+	LLVector4a ry1( pos1[VY], tex1.mV[VX], tex1.mV[VY] );
+	LLVector4a ry2( pos2[VY], tex2.mV[VX], tex2.mV[VY] );
 
-	LLVector3 rz0( pos0.mV[VZ], tex0.mV[VX], tex0.mV[VY] );
-	LLVector3 rz1( pos1.mV[VZ], tex1.mV[VX], tex1.mV[VY] );
-	LLVector3 rz2( pos2.mV[VZ], tex2.mV[VX], tex2.mV[VY] );
+	LLVector4a rz0( pos0[VZ], tex0.mV[VX], tex0.mV[VY] );
+	LLVector4a rz1( pos1[VZ], tex1.mV[VX], tex1.mV[VY] );
+	LLVector4a rz2( pos2[VZ], tex2.mV[VX], tex2.mV[VY] );
 	
-	LLVector3 r0 = (rx0 - rx1) % (rx0 - rx2);
-	LLVector3 r1 = (ry0 - ry1) % (ry0 - ry2);
-	LLVector3 r2 = (rz0 - rz1) % (rz0 - rz2);
+	LLVector4a lhs, rhs;
+
+	LLVector4a r0; 
+	lhs.setSub(rx0, rx1); rhs.setSub(rx0, rx2);
+	r0.setCross3(lhs, rhs);
+		
+	LLVector4a r1;
+	lhs.setSub(ry0, ry1); rhs.setSub(ry0, ry2);
+	r1.setCross3(lhs, rhs);
+
+	LLVector4a r2;
+	lhs.setSub(rz0, rz1); rhs.setSub(rz0, rz2);
+	r2.setCross3(lhs, rhs);
 
-	if( r0.mV[VX] && r1.mV[VX] && r2.mV[VX] )
+	if( r0[VX] && r1[VX] && r2[VX] )
 	{
-		LLVector3 binormal(
-				-r0.mV[VZ] / r0.mV[VX],
-				-r1.mV[VZ] / r1.mV[VX],
-				-r2.mV[VZ] / r2.mV[VX]);
+		binormal.set(
+				-r0[VZ] / r0[VX],
+				-r1[VZ] / r1[VX],
+				-r2[VZ] / r2[VX]);
 		// binormal.normVec();
-		return binormal;
 	}
 	else
 	{
-		return LLVector3( 0, 1 , 0 );
+		binormal.set( 0, 1 , 0 );
 	}
 }
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 28b9895ff39ce3f3e347590ff7873a8512d779d5..01bfbd858be72fd6badd82b8e0e335f68b682eba 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -34,8 +34,13 @@ class LLPathParams;
 class LLVolumeParams;
 class LLProfile;
 class LLPath;
+
+template <class T> class LLOctreeNode;
+
+class LLVector4a;
 class LLVolumeFace;
 class LLVolume;
+class LLVolumeTriangle;
 
 #include "lldarray.h"
 #include "lluuid.h"
@@ -43,6 +48,8 @@ class LLVolume;
 //#include "vmath.h"
 #include "v2math.h"
 #include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
 #include "llquaternion.h"
 #include "llstrider.h"
 #include "v4coloru.h"
@@ -177,12 +184,14 @@ const U8 LL_SCULPT_TYPE_SPHERE    = 1;
 const U8 LL_SCULPT_TYPE_TORUS     = 2;
 const U8 LL_SCULPT_TYPE_PLANE     = 3;
 const U8 LL_SCULPT_TYPE_CYLINDER  = 4;
-
-const U8 LL_SCULPT_TYPE_MASK      = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | LL_SCULPT_TYPE_CYLINDER;
+const U8 LL_SCULPT_TYPE_MESH      = 5;
+const U8 LL_SCULPT_TYPE_MASK      = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE |
+	LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH;
 
 const U8 LL_SCULPT_FLAG_INVERT    = 64;
 const U8 LL_SCULPT_FLAG_MIRROR    = 128;
 
+const S32 LL_SCULPT_MESH_MAX_FACES = 8;
 
 class LLProfileParams
 {
@@ -569,6 +578,9 @@ class LLVolumeParams
 	BOOL importLegacyStream(std::istream& input_stream);
 	BOOL exportLegacyStream(std::ostream& output_stream) const;
 
+	LLSD sculptAsLLSD() const;
+	bool sculptFromLLSD(LLSD& sd);
+	
 	LLSD asLLSD() const;
 	operator LLSD() const { return asLLSD(); }
 	bool fromLLSD(LLSD& sd);
@@ -628,7 +640,8 @@ class LLVolumeParams
 	const F32&  getSkew() const			{ return mPathParams.getSkew();			}
 	const LLUUID& getSculptID() const	{ return mSculptID;						}
 	const U8& getSculptType() const     { return mSculptType;                   }
-
+	bool isSculpt() const;
+	bool isMeshSculpt() const;
 	BOOL isConvex() const;
 
 	// 'begin' and 'end' should be in range [0, 1] (they will be clamped)
@@ -779,30 +792,88 @@ class LLDynamicPath : public LLPath
 class LLVolumeFace
 {
 public:
-	LLVolumeFace() : 
-		mID(0),
-		mTypeMask(0),
-		mHasBinormals(FALSE),
-		mBeginS(0),
-		mBeginT(0),
-		mNumS(0),
-		mNumT(0)
+	class VertexData
 	{
-	}
+		enum 
+		{
+			POSITION = 0,
+			NORMAL = 1
+		};
+
+	private:
+		void init();
+	public:
+		VertexData();
+		VertexData(const VertexData& rhs);
+		const VertexData& operator=(const VertexData& rhs);
+
+		~VertexData();
+		LLVector4a& getPosition();
+		LLVector4a& getNormal();
+		const LLVector4a& getPosition() const;
+		const LLVector4a& getNormal() const;
+		void setPosition(const LLVector4a& pos);
+		void setNormal(const LLVector4a& norm);
+		
+
+		LLVector2 mTexCoord;
+
+		bool operator<(const VertexData& rhs) const;
+		bool operator==(const VertexData& rhs) const;
+		bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const;
+
+	private:
+		LLVector4a* mData;
+	};
+
+	LLVolumeFace();
+	LLVolumeFace(const LLVolumeFace& src);
+	LLVolumeFace& operator=(const LLVolumeFace& rhs);
+
+	~LLVolumeFace();
+private:
+	void freeData();
+public:
 
 	BOOL create(LLVolume* volume, BOOL partial_build = FALSE);
 	void createBinormals();
-	void makeTriStrip();
 	
-	class VertexData
+	void appendFace(const LLVolumeFace& face, LLMatrix4& transform, LLMatrix4& normal_tranform);
+
+	void resizeVertices(S32 num_verts);
+	void allocateBinormals(S32 num_verts);
+	void allocateWeights(S32 num_verts);
+	void resizeIndices(S32 num_indices);
+	void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx);
+
+	void pushVertex(const VertexData& cv);
+	void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc);
+	void pushIndex(const U16& idx);
+
+	void swapData(LLVolumeFace& rhs);
+
+	void getVertexData(U16 indx, LLVolumeFace::VertexData& cv);
+
+	class VertexMapData : public LLVolumeFace::VertexData
 	{
 	public:
-		LLVector3 mPosition;
-		LLVector3 mNormal;
-		LLVector3 mBinormal;
-		LLVector2 mTexCoord;
+		U16 mIndex;
+
+		bool operator==(const LLVolumeFace::VertexData& rhs) const;
+
+		struct ComparePosition
+		{
+			bool operator()(const LLVector3& a, const LLVector3& b) const;
+		};
+
+		typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap;
 	};
 
+	void optimize(F32 angle_cutoff = 2.f);
+	void cacheOptimize();
+
+	void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
+
 	enum
 	{
 		SINGLE_MASK =	0x0001,
@@ -821,23 +892,35 @@ class LLVolumeFace
 public:
 	S32 mID;
 	U32 mTypeMask;
-	LLVector3 mCenter;
-	BOOL mHasBinormals;
-
+	
 	// Only used for INNER/OUTER faces
 	S32 mBeginS;
 	S32 mBeginT;
 	S32 mNumS;
 	S32 mNumT;
 
-	LLVector3 mExtents[2]; //minimum and maximum point of face
-	LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face.
+	LLVector4a* mExtents; //minimum and maximum point of face
+	LLVector4a* mCenter;
+	LLVector2   mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face.
+
+	S32 mNumVertices;
+	S32 mNumIndices;
+
+	LLVector4a* mPositions;
+	LLVector4a* mNormals;
+	LLVector4a* mBinormals;
+	LLVector2*  mTexCoords;
+	U16* mIndices;
 
-	std::vector<VertexData> mVertices;
-	std::vector<U16>	mIndices;
-	std::vector<U16>	mTriStrip;
 	std::vector<S32>	mEdge;
 
+	//list of skin weights for rigged volumes
+	// format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight>
+	// mWeights.size() should be empty or match mVertices.size()  
+	LLVector4a* mWeights;
+
+	LLOctreeNode<LLVolumeTriangle>* mOctree;
+
 private:
 	BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
 	BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE);
@@ -848,8 +931,7 @@ class LLVolume : public LLRefCount
 {
 	friend class LLVolumeLODGroup;
 
-private:
-	LLVolume(const LLVolume&);  // Don't implement
+protected:
 	~LLVolume(); // use unref
 
 public:
@@ -871,7 +953,7 @@ class LLVolume : public LLRefCount
 	
 	U8 getProfileType()	const								{ return mParams.getProfileParams().getCurveType(); }
 	U8 getPathType() const									{ return mParams.getPathParams().getCurveType(); }
-	S32	getNumFaces() const									{ return (S32)mProfilep->mFaces.size(); }
+	S32	getNumFaces() const;
 	S32 getNumVolumeFaces() const							{ return mVolumeFaces.size(); }
 	F32 getDetail() const									{ return mDetail; }
 	const LLVolumeParams& getParams() const					{ return mParams; }
@@ -893,15 +975,17 @@ class LLVolume : public LLRefCount
 	BOOL isUnique() const									{ return mUnique; }
 
 	S32 getSculptLevel() const                              { return mSculptLevel; }
-	
+	void setSculptLevel(S32 level)							{ mSculptLevel = level; }
+
 	S32 *getTriangleIndices(U32 &num_indices) const;
 
 	// returns number of triangle indeces required for path/profile mesh
 	S32 getNumTriangleIndices() const;
 
+	S32 getNumTriangles() const;
+
 	void generateSilhouetteVertices(std::vector<LLVector3> &vertices, 
 									std::vector<LLVector3> &normals, 
-									std::vector<S32> &segments, 
 									const LLVector3& view_vec,
 									const LLMatrix4& mat,
 									const LLMatrix3& norm_mat,
@@ -917,6 +1001,13 @@ class LLVolume : public LLRefCount
 							 LLVector3* normal = NULL,               // return the surface normal at the intersection point
 							 LLVector3* bi_normal = NULL             // return the surface bi-normal at the intersection point
 		);
+
+	S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
+								   S32 face = 1,
+								   LLVector3* intersection = NULL,
+								   LLVector2* tex_coord = NULL,
+								   LLVector3* normal = NULL,
+								   LLVector3* bi_normal = NULL);
 	
 	// The following cleans up vertices and triangles,
 	// getting rid of degenerate triangles and duplicate vertices,
@@ -938,11 +1029,14 @@ class LLVolume : public LLRefCount
 	friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep);		// HACK to bypass Windoze confusion over 
 																				// conversion if *(LLVolume*) to LLVolume&
 	const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
-
+	
 	U32					mFaceMask;			// bit array of which faces exist in this volume
 	LLVector3			mLODScaleBias;		// vector for biasing LOD based on scale
 	
 	void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level);
+	void copyVolumeFaces(const LLVolume* volume);
+	void cacheOptimize();
+
 private:
 	void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type);
 	F32 sculptGetSurfaceArea();
@@ -953,35 +1047,56 @@ class LLVolume : public LLRefCount
 protected:
 	BOOL generate();
 	void createVolumeFaces();
+public:
+	virtual bool unpackVolumeFaces(std::istream& is, S32 size);
+
+	virtual void makeTetrahedron();
+	virtual BOOL isTetrahedron();
 
  protected:
 	BOOL mUnique;
 	F32 mDetail;
 	S32 mSculptLevel;
+	BOOL mIsTetrahedron;
 	
 	LLVolumeParams mParams;
 	LLPath *mPathp;
 	LLProfile *mProfilep;
 	std::vector<Point> mMesh;
-
+	
 	BOOL mGenerateSingleFace;
 	typedef std::vector<LLVolumeFace> face_list_t;
 	face_list_t mVolumeFaces;
+
+public:
+	LLVector4a* mHullPoints;
+	U16* mHullIndices;
+	S32 mNumHullPoints;
+	S32 mNumHullIndices;
 };
 
 std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
 
-LLVector3 calc_binormal_from_triangle(
-		const LLVector3& pos0,
+void calc_binormal_from_triangle(
+		LLVector4a& binormal,
+		const LLVector4a& pos0,
 		const LLVector2& tex0,
-		const LLVector3& pos1,
+		const LLVector4a& pos1,
 		const LLVector2& tex1,
-		const LLVector3& pos2,
+		const LLVector4a& pos2,
 		const LLVector2& tex2);
 
+BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size);
 BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
+BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size);
+
 BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
-							F32* intersection_a, F32* intersection_b, F32* intersection_t, BOOL two_sided);
+							F32& intersection_a, F32& intersection_b, F32& intersection_t, BOOL two_sided);
+
+BOOL LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+							F32& intersection_a, F32& intersection_b, F32& intersection_t);
+BOOL LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+							F32& intersection_a, F32& intersection_b, F32& intersection_t);
 	
 	
 
diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp
index 88c195936cd87dcde745b441fee01d49938f7981..c60b75008837f770c63c324e57f917f5f6e97153 100644
--- a/indra/llmath/llvolumemgr.cpp
+++ b/indra/llmath/llvolumemgr.cpp
@@ -314,7 +314,7 @@ BOOL LLVolumeLODGroup::derefLOD(LLVolume *volumep)
 		{
 			llassert_always(mLODRefs[i] > 0);
 			mLODRefs[i]--;
-#if 1 // SJB: Possible opt: keep other lods around
+#if 0 // SJB: Possible opt: keep other lods around
 			if (!mLODRefs[i])
 			{
 				mVolumeLODs[i] = NULL;
@@ -369,6 +369,19 @@ F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail)
 	return mDetailScales[detail];
 }
 
+S32 LLVolumeLODGroup::getVolumeDetailFromScale(const F32 detail)
+{
+	for (S32 i = 1; i < 4; i++)
+	{
+		if (mDetailScales[i] > detail)
+		{
+			return i-1;
+		}
+	}
+
+	return 3;
+}
+
 F32 LLVolumeLODGroup::dump()
 {
 	F32 usage = 0.f;
diff --git a/indra/llmath/llvolumemgr.h b/indra/llmath/llvolumemgr.h
index 5257da2693d81d6f82600cc556d3a3164996f63d..c75906f675ff8fa7b484432d2adac78bdaa11f10 100644
--- a/indra/llmath/llvolumemgr.h
+++ b/indra/llmath/llvolumemgr.h
@@ -53,6 +53,7 @@ class LLVolumeLODGroup
 	static S32 getDetailFromTan(const F32 tan_angle);
 	static void getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher);
 	static F32 getVolumeScaleFromDetail(const S32 detail);
+	static S32 getVolumeDetailFromScale(F32 scale);
 
 	LLVolume* refLOD(const S32 detail);
 	BOOL derefLOD(LLVolume *volumep);
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5a935c2b54133710c8d39c1369cce3de9eca07a
--- /dev/null
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -0,0 +1,256 @@
+/** 
+
+ * @file llvolumeoctree.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llvolumeoctree.h"
+#include "llvector4a.h"
+
+BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size)
+{
+	LLVector4a fAWdU;
+	LLVector4a dir;
+	LLVector4a diff;
+
+	dir.setSub(end, start);
+	dir.mul(0.5f);
+
+	diff.setAdd(end,start);
+	diff.mul(0.5f);
+	diff.sub(center);
+	fAWdU.setAbs(dir); 
+
+	LLVector4a rhs;
+	rhs.setAdd(size, fAWdU);
+
+	LLVector4a lhs;
+	lhs.setAbs(diff);
+
+	U32 grt = lhs.greaterThan(rhs).getGatheredBits();
+
+	if (grt & 0x7)
+	{
+		return false;
+	}
+	
+	LLVector4a f;
+	f.setCross3(dir, diff);
+	f.setAbs(f);
+
+	LLVector4a v0, v1;
+
+	v0 = _mm_shuffle_ps(size, size,_MM_SHUFFLE(3,0,0,1));
+	v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,1,2,2));
+	lhs.setMul(v0, v1);
+
+	v0 = _mm_shuffle_ps(size, size, _MM_SHUFFLE(3,1,2,2));
+	v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,0,0,1));
+	rhs.setMul(v0, v1);
+	rhs.add(lhs);
+	
+	grt = f.greaterThan(rhs).getGatheredBits();
+
+	return (grt & 0x7) ? false : true;
+}
+
+
+LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node)
+{
+	node->addListener(this);
+}
+
+LLVolumeOctreeListener::~LLVolumeOctreeListener()
+{
+
+}
+	
+void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent, 
+	LLOctreeNode<LLVolumeTriangle>* child)
+{
+	new LLVolumeOctreeListener(child);
+}
+
+
+LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, 
+							   const LLVolumeFace* face, F32* closest_t,
+							   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+   : mFace(face),
+     mStart(start),
+	 mDir(dir),
+	 mIntersection(intersection),
+	 mTexCoord(tex_coord),
+	 mNormal(normal),
+	 mBinormal(bi_normal),
+	 mClosestT(closest_t),
+	 mHitFace(false)
+{
+	mEnd.setAdd(mStart, mDir);
+}
+
+void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>* node)
+{
+	LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
+
+	/*const F32* start = mStart.getF32();
+	const F32* end = mEnd.getF32();
+	const F32* center = vl->mBounds[0].getF32();
+	const F32* size = vl->mBounds[1].getF32();*/
+
+	//if (LLLineSegmentBoxIntersect(mStart, mEnd, vl->mBounds[0], vl->mBounds[1]))
+	if (LLLineSegmentBoxIntersect(mStart.getF32ptr(), mEnd.getF32ptr(), vl->mBounds[0].getF32ptr(), vl->mBounds[1].getF32ptr()))
+	{
+		node->accept(this);
+		for (S32 i = 0; i < node->getChildCount(); ++i)
+		{
+			traverse(node->getChild(i));
+		}
+	}
+}
+
+void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* node)
+{
+	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = 
+			node->getData().begin(); iter != node->getData().end(); ++iter)
+	{
+		const LLVolumeTriangle* tri = *iter;
+
+		F32 a, b, t;
+		
+		if (LLTriangleRayIntersect(*tri->mV[0], *tri->mV[1], *tri->mV[2],
+				mStart, mDir, a, b, t))
+		{
+			if ((t >= 0.f) &&      // if hit is after start
+				(t <= 1.f) &&      // and before end
+				(t < *mClosestT))   // and this hit is closer
+			{
+				*mClosestT = t;
+				mHitFace = true;
+
+				if (mIntersection != NULL)
+				{
+					LLVector4a intersect = mDir;
+					intersect.mul(*mClosestT);
+					intersect.add(mStart);
+					mIntersection->set(intersect.getF32ptr());
+				}
+
+
+				if (mTexCoord != NULL)
+				{
+					LLVector2* tc = (LLVector2*) mFace->mTexCoords;
+					*mTexCoord = ((1.f - a - b)  * tc[tri->mIndex[0]] +
+						a              * tc[tri->mIndex[1]] +
+						b              * tc[tri->mIndex[2]]);
+
+				}
+
+				if (mNormal != NULL)
+				{
+					LLVector4* norm = (LLVector4*) mFace->mNormals;
+
+					*mNormal    = ((1.f - a - b)  * LLVector3(norm[tri->mIndex[0]]) + 
+						a              * LLVector3(norm[tri->mIndex[1]]) +
+						b              * LLVector3(norm[tri->mIndex[2]]));
+				}
+
+				if (mBinormal != NULL)
+				{
+					LLVector4* binormal = (LLVector4*) mFace->mBinormals;
+					*mBinormal = ((1.f - a - b)  * LLVector3(binormal[tri->mIndex[0]]) + 
+							a              * LLVector3(binormal[tri->mIndex[1]]) +
+							b              * LLVector3(binormal[tri->mIndex[2]]));
+				}
+			}
+		}
+	}
+}
+
+const LLVector4a& LLVolumeTriangle::getPositionGroup() const
+{
+	return mPositionGroup;
+}
+
+const F32& LLVolumeTriangle::getBinRadius() const
+{
+	return mRadius;
+}
+
+
+//TEST CODE
+
+void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+{
+	LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
+
+	//make sure bounds matches extents
+	LLVector4a& min = node->mExtents[0];
+	LLVector4a& max = node->mExtents[1];
+
+	LLVector4a& center = node->mBounds[0];
+	LLVector4a& size = node->mBounds[1];
+
+	LLVector4a test_min, test_max;
+	test_min.setSub(center, size);
+	test_max.setAdd(center, size);
+
+	if (!test_min.equals3(min, 0.001f) ||
+		!test_max.equals3(max, 0.001f))
+	{
+		llerrs << "Bad bounding box data found." << llendl;
+	}
+
+	test_min.sub(LLVector4a(0.001f));
+	test_max.add(LLVector4a(0.001f));
+
+	for (U32 i = 0; i < branch->getChildCount(); ++i)
+	{
+		LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
+
+		//make sure all children fit inside this node
+		if (child->mExtents[0].lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ) ||
+			child->mExtents[1].greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ))
+		{
+			llerrs << "Child protrudes from bounding box." << llendl;
+		}
+	}
+
+	//children fit, check data
+	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getData().begin(); 
+			iter != branch->getData().end(); ++iter)
+	{
+		const LLVolumeTriangle* tri = *iter;
+
+		//validate triangle
+		for (U32 i = 0; i < 3; i++)
+		{
+			if (tri->mV[i]->greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ) ||
+				tri->mV[i]->lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ))
+			{
+				llerrs << "Triangle protrudes from node." << llendl;
+			}
+		}
+	}
+}
+
+
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
new file mode 100644
index 0000000000000000000000000000000000000000..688d91dc40b6c0f805187d808d45e1e9644aba42
--- /dev/null
+++ b/indra/llmath/llvolumeoctree.h
@@ -0,0 +1,134 @@
+/** 
+ * @file llvolumeoctree.h
+ * @brief LLVolume octree classes.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVOLUME_OCTREE_H
+#define LL_LLVOLUME_OCTREE_H
+
+#include "linden_common.h"
+#include "llmemory.h"
+
+#include "lloctree.h"
+#include "llvolume.h"
+#include "llvector4a.h"
+
+class LLVolumeTriangle : public LLRefCount
+{
+public:
+	LLVolumeTriangle()
+	{
+		
+	}
+
+	LLVolumeTriangle(const LLVolumeTriangle& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
+	~LLVolumeTriangle()
+	{
+	
+	}
+
+	LLVector4a mPositionGroup;
+
+	const LLVector4a* mV[3];
+	U16 mIndex[3];
+
+	F32 mRadius;
+
+	virtual const LLVector4a& getPositionGroup() const;
+	virtual const F32& getBinRadius() const;
+};
+
+class LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle>
+{
+public:
+	
+	LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node);
+	~LLVolumeOctreeListener();
+	
+	LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLVolumeOctreeListener& operator=(const LLVolumeOctreeListener& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
+	 //LISTENER FUNCTIONS
+	virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent, 
+		LLOctreeNode<LLVolumeTriangle>* child);
+	virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { }
+	virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle>* parent, 
+			const LLOctreeNode<LLVolumeTriangle>* child) {	}
+	virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
+	virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
+	virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { }
+	
+
+public:
+	LLVector4a mBounds[2]; // bounding box (center, size) of this node and all its children (tight fit to objects)
+	LLVector4a mExtents[2]; // extents (min, max) of this node and all its children
+};
+
+class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle>
+{
+public:
+	const LLVolumeFace* mFace;
+	LLVector4a mStart;
+	LLVector4a mDir;
+	LLVector4a mEnd;
+	LLVector3* mIntersection;
+	LLVector2* mTexCoord;
+	LLVector3* mNormal;
+	LLVector3* mBinormal;
+	F32* mClosestT;
+	bool mHitFace;
+
+	LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, 
+								   const LLVolumeFace* face, F32* closest_t,
+								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal);
+
+	void traverse(const LLOctreeNode<LLVolumeTriangle>* node);
+
+	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* node);
+};
+
+class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle>
+{
+	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch);
+};
+
+#endif
diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp
index 946b1553fe16a3b7e5b519ea948ee9888b13128d..bad4deb4dee87498382345aa2b8da3578c80d667 100644
--- a/indra/llmath/m4math.cpp
+++ b/indra/llmath/m4math.cpp
@@ -215,8 +215,33 @@ const LLMatrix4&	LLMatrix4::transpose()
 
 F32 LLMatrix4::determinant() const
 {
-	llerrs << "Not implemented!" << llendl;
-	return 0.f;
+	F32 value =
+	    mMatrix[0][3] * mMatrix[1][2] * mMatrix[2][1] * mMatrix[3][0] -
+	    mMatrix[0][2] * mMatrix[1][3] * mMatrix[2][1] * mMatrix[3][0] -
+	    mMatrix[0][3] * mMatrix[1][1] * mMatrix[2][2] * mMatrix[3][0] +
+	    mMatrix[0][1] * mMatrix[1][3] * mMatrix[2][2] * mMatrix[3][0] +
+	    mMatrix[0][2] * mMatrix[1][1] * mMatrix[2][3] * mMatrix[3][0] -
+	    mMatrix[0][1] * mMatrix[1][2] * mMatrix[2][3] * mMatrix[3][0] -
+	    mMatrix[0][3] * mMatrix[1][2] * mMatrix[2][0] * mMatrix[3][1] +
+	    mMatrix[0][2] * mMatrix[1][3] * mMatrix[2][0] * mMatrix[3][1] +
+	    mMatrix[0][3] * mMatrix[1][0] * mMatrix[2][2] * mMatrix[3][1] -
+	    mMatrix[0][0] * mMatrix[1][3] * mMatrix[2][2] * mMatrix[3][1] -
+	    mMatrix[0][2] * mMatrix[1][0] * mMatrix[2][3] * mMatrix[3][1] +
+	    mMatrix[0][0] * mMatrix[1][2] * mMatrix[2][3] * mMatrix[3][1] +
+	    mMatrix[0][3] * mMatrix[1][1] * mMatrix[2][0] * mMatrix[3][2] -
+	    mMatrix[0][1] * mMatrix[1][3] * mMatrix[2][0] * mMatrix[3][2] -
+	    mMatrix[0][3] * mMatrix[1][0] * mMatrix[2][1] * mMatrix[3][2] +
+	    mMatrix[0][0] * mMatrix[1][3] * mMatrix[2][1] * mMatrix[3][2] +
+	    mMatrix[0][1] * mMatrix[1][0] * mMatrix[2][3] * mMatrix[3][2] -
+	    mMatrix[0][0] * mMatrix[1][1] * mMatrix[2][3] * mMatrix[3][2] -
+	    mMatrix[0][2] * mMatrix[1][1] * mMatrix[2][0] * mMatrix[3][3] +
+	    mMatrix[0][1] * mMatrix[1][2] * mMatrix[2][0] * mMatrix[3][3] +
+	    mMatrix[0][2] * mMatrix[1][0] * mMatrix[2][1] * mMatrix[3][3] -
+	    mMatrix[0][0] * mMatrix[1][2] * mMatrix[2][1] * mMatrix[3][3] -
+	    mMatrix[0][1] * mMatrix[1][0] * mMatrix[2][2] * mMatrix[3][3] +
+		mMatrix[0][0] * mMatrix[1][1] * mMatrix[2][2] * mMatrix[3][3];
+
+	return value;
 }
 
 // Only works for pure orthonormal, homogeneous transform matrices.
@@ -422,6 +447,17 @@ const LLMatrix4&  	LLMatrix4::initRotTrans(const LLQuaternion &q, const LLVector
 	return (*this);
 }
 
+const LLMatrix4& LLMatrix4::initScale(const LLVector3 &scale)
+{
+	setIdentity();
+
+	mMatrix[VX][VX] = scale.mV[VX];
+	mMatrix[VY][VY] = scale.mV[VY];
+	mMatrix[VZ][VZ] = scale.mV[VZ];
+	
+	return (*this);
+}
+
 const LLMatrix4& LLMatrix4::initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos)
 {
 	F32		sx, sy, sz;
@@ -642,37 +678,6 @@ const LLMatrix4&  	LLMatrix4::initMatrix(const LLMatrix3 &mat, const LLVector4 &
 
 // LLMatrix4 Operators
 
-
-/* Not implemented to help enforce code consistency with the syntax of
-   row-major notation.  This is a Good Thing.
-LLVector4 operator*(const LLMatrix4 &a, const LLVector4 &b)
-{
-	// Operate "to the right" on column-vector b
-	LLVector4	vec;
-	vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] + 
-				 a.mMatrix[VY][VX] * b.mV[VY] + 
- 				 a.mMatrix[VZ][VX] * b.mV[VZ] +
-				 a.mMatrix[VW][VX] * b.mV[VW];
-
-	vec.mV[VY] = a.mMatrix[VX][VY] * b.mV[VX] + 
-				 a.mMatrix[VY][VY] * b.mV[VY] + 
-				 a.mMatrix[VZ][VY] * b.mV[VZ] +
-				 a.mMatrix[VW][VY] * b.mV[VW];
-
-	vec.mV[VZ] = a.mMatrix[VX][VZ] * b.mV[VX] + 
-			  	 a.mMatrix[VY][VZ] * b.mV[VY] + 
-				 a.mMatrix[VZ][VZ] * b.mV[VZ] +
-				 a.mMatrix[VW][VZ] * b.mV[VW];
-
-	vec.mV[VW] = a.mMatrix[VX][VW] * b.mV[VX] + 
-				 a.mMatrix[VY][VW] * b.mV[VY] + 
-				 a.mMatrix[VZ][VW] * b.mV[VZ] +
-				 a.mMatrix[VW][VW] * b.mV[VW];
-	return vec;
-}
-*/
-
-
 LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b)
 {
 	// Operate "to the left" on row-vector a
@@ -768,6 +773,23 @@ bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b)
 	return FALSE;
 }
 
+bool operator<(const LLMatrix4& a, const LLMatrix4 &b)
+{
+	U32		i, j;
+	for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+	{
+		for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+		{
+			if (a.mMatrix[i][j] != b.mMatrix[i][j])
+			{
+				return a.mMatrix[i][j] < b.mMatrix[i][j];
+			}
+		}
+	}
+
+	return false;
+}
+
 const LLMatrix4& operator*=(LLMatrix4 &a, F32 k)
 {
 	U32		i, j;
@@ -807,4 +829,54 @@ std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a)
 	return s;
 }
 
+LLSD LLMatrix4::getValue() const
+{
+	LLSD ret;
+	
+	ret[0] = mMatrix[0][0];
+	ret[1] = mMatrix[0][1];
+	ret[2] = mMatrix[0][2];
+	ret[3] = mMatrix[0][3];
+
+	ret[4] = mMatrix[1][0];
+	ret[5] = mMatrix[1][1];
+	ret[6] = mMatrix[1][2];
+	ret[7] = mMatrix[1][3];
+
+	ret[8] = mMatrix[2][0];
+	ret[9] = mMatrix[2][1];
+	ret[10] = mMatrix[2][2];
+	ret[11] = mMatrix[2][3];
+
+	ret[12] = mMatrix[3][0];
+	ret[13] = mMatrix[3][1];
+	ret[14] = mMatrix[3][2];
+	ret[15] = mMatrix[3][3];
+
+	return ret;
+}
+
+void LLMatrix4::setValue(const LLSD& data) 
+{
+	mMatrix[0][0] = data[0].asReal();
+	mMatrix[0][1] = data[1].asReal();
+	mMatrix[0][2] = data[2].asReal();
+	mMatrix[0][3] = data[3].asReal();
+
+	mMatrix[1][0] = data[4].asReal();
+	mMatrix[1][1] = data[5].asReal();
+	mMatrix[1][2] = data[6].asReal();
+	mMatrix[1][3] = data[7].asReal();
+
+	mMatrix[2][0] = data[8].asReal();
+	mMatrix[2][1] = data[9].asReal();
+	mMatrix[2][2] = data[10].asReal();
+	mMatrix[2][3] = data[11].asReal();
+
+	mMatrix[3][0] = data[12].asReal();
+	mMatrix[3][1] = data[13].asReal();
+	mMatrix[3][2] = data[14].asReal();
+	mMatrix[3][3] = data[15].asReal();
+}
+
 
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
index 6ec99584916d2e7a00f3bc1cfa354aec52f93085..a7dce10397839e4ea21c88d59f4689d29f9193ed 100644
--- a/indra/llmath/m4math.h
+++ b/indra/llmath/m4math.h
@@ -119,6 +119,8 @@ class LLMatrix4
 
 	~LLMatrix4(void);										// Destructor
 
+	LLSD getValue() const;
+	void setValue(const LLSD&);
 
 	//////////////////////////////
 	//
@@ -132,6 +134,7 @@ class LLMatrix4
 
 	// various useful matrix functions
 	const LLMatrix4& setIdentity();					// Load identity matrix
+	bool isIdentity() const;
 	const LLMatrix4& setZero();						// Clears matrix to all zeros.
 
 	const LLMatrix4& initRotation(const F32 angle, const F32 x, const F32 y, const F32 z);	// Calculate rotation matrix by rotating angle radians about (x, y, z)
@@ -153,6 +156,7 @@ class LLMatrix4
 	const LLMatrix4& initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos); // Rotation from Euler + translation
 	const LLMatrix4& initRotTrans(const LLQuaternion &q, const LLVector4 &pos);	// Set with Quaternion and position
 
+	const LLMatrix4& initScale(const LLVector3 &scale);
 
 	// Set all
 	const LLMatrix4& initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos);	
@@ -219,10 +223,7 @@ class LLMatrix4
 	// Operators
 	//
 
-// Not implemented to enforce code that agrees with symbolic syntax
-//		friend LLVector4 operator*(const LLMatrix4 &a, const LLVector4 &b);		// Apply rotation a to vector b
-
-//	friend inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b);		// Return a * b
+	//	friend inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b);		// Return a * b
 	friend LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b);		// Return transform of vector a by matrix b
 	friend const LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b);		// Return full transform of a by matrix b
 	friend LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b);	// Rotates a but does not translate
@@ -230,6 +231,7 @@ class LLMatrix4
 
 	friend bool operator==(const LLMatrix4 &a, const LLMatrix4 &b);			// Return a == b
 	friend bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b);			// Return a != b
+	friend bool operator<(const LLMatrix4 &a, const LLMatrix4& b);			// Return a < b
 
 	friend const LLMatrix4& operator+=(LLMatrix4 &a, const LLMatrix4 &b);	// Return a + b
 	friend const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b);	// Return a - b
@@ -263,6 +265,30 @@ inline const LLMatrix4&	LLMatrix4::setIdentity()
 	return (*this);
 }
 
+inline bool LLMatrix4::isIdentity() const
+{
+	return
+		mMatrix[0][0] == 1.f &&
+		mMatrix[0][1] == 0.f &&
+		mMatrix[0][2] == 0.f &&
+		mMatrix[0][3] == 0.f &&
+
+		mMatrix[1][0] == 0.f &&
+		mMatrix[1][1] == 1.f &&
+		mMatrix[1][2] == 0.f &&
+		mMatrix[1][3] == 0.f &&
+
+		mMatrix[2][0] == 0.f &&
+		mMatrix[2][1] == 0.f &&
+		mMatrix[2][2] == 1.f &&
+		mMatrix[2][3] == 0.f &&
+
+		mMatrix[3][0] == 0.f &&
+		mMatrix[3][1] == 0.f &&
+		mMatrix[3][2] == 0.f &&
+		mMatrix[3][3] == 1.f;
+}
+
 
 /*
 inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b)
diff --git a/indra/llmath/tests/llquaternion_test.cpp b/indra/llmath/tests/llquaternion_test.cpp
index 9e79b299ff07f21f61e743c7c03f16d72a29d04c..e69010b2d63e17f6121343e674381cc81e3925d9 100644
--- a/indra/llmath/tests/llquaternion_test.cpp
+++ b/indra/llmath/tests/llquaternion_test.cpp
@@ -29,12 +29,12 @@
 #include "linden_common.h"
 #include "../test/lltut.h"
 
-#include "../llquaternion.h"
 #include "../v4math.h"
 #include "../v3math.h"
 #include "../v3dmath.h"
 #include "../m4math.h"
 #include "../m3math.h"
+#include "../llquaternion.h"
 
 namespace tut
 {
diff --git a/indra/llmath/tests/v2math_test.cpp b/indra/llmath/tests/v2math_test.cpp
index 9747996b2561fc11fa03473302a764f38f1f496c..4d6a2eca938be201d92fcce92ddab7550b567983 100644
--- a/indra/llmath/tests/v2math_test.cpp
+++ b/indra/llmath/tests/v2math_test.cpp
@@ -85,7 +85,7 @@ namespace tut
 		F32 x = 2.2345f, y = 3.5678f ;
 		LLVector2 vec2(x,y);
 		ensure("magVecSquared:Fail ", is_approx_equal(vec2.magVecSquared(), (x*x + y*y)));
-		ensure("magVec:Fail ", is_approx_equal(vec2.magVec(), fsqrtf(x*x + y*y)));
+		ensure("magVec:Fail ", is_approx_equal(vec2.magVec(), (F32) sqrt(x*x + y*y)));
 	}
 
 	template<> template<>
@@ -407,7 +407,7 @@ namespace tut
 		ensure_equals("dist_vec_squared values are not equal",val2, val1);
 
 		val1 = 	dist_vec(vec2, vec3);
-		val2 = fsqrtf((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2));
+		val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2));
 		ensure_equals("dist_vec values are not equal",val2, val1);
 	}
 
@@ -431,7 +431,7 @@ namespace tut
 		LLVector2 vec2(x1, y1);
 
 		F32 vecMag = vec2.normVec();
-		F32 mag = fsqrtf(x1*x1 + y1*y1);
+		F32 mag = (F32) sqrt(x1*x1 + y1*y1);
 
 		F32 oomag = 1.f / mag;
 		val1 = x1 * oomag;
diff --git a/indra/llmath/tests/v3color_test.cpp b/indra/llmath/tests/v3color_test.cpp
index 2c00f00ab3517de30d390fc8a7af806c259d89af..29d1c483abdca4b285f6842ec7080e86eaf2570a 100644
--- a/indra/llmath/tests/v3color_test.cpp
+++ b/indra/llmath/tests/v3color_test.cpp
@@ -93,7 +93,7 @@ namespace tut
 		F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f;
 		LLColor3 llcolor3(r,g,b);
 		ensure("magVecSquared:Fail ", is_approx_equal(llcolor3.magVecSquared(), (r*r + g*g + b*b)));
-		ensure("magVec:Fail ", is_approx_equal(llcolor3.magVec(), fsqrtf(r*r + g*g + b*b)));
+		ensure("magVec:Fail ", is_approx_equal(llcolor3.magVec(), (F32) sqrt(r*r + g*g + b*b)));
 	}
 
 	template<> template<>
@@ -103,7 +103,7 @@ namespace tut
 		F32 val1, val2,val3;
 		LLColor3 llcolor3(r,g,b);
 		F32 vecMag = llcolor3.normVec();
-		F32 mag = fsqrtf(r*r + g*g + b*b);
+		F32 mag = (F32) sqrt(r*r + g*g + b*b);
 		F32 oomag = 1.f / mag;
 		val1 = r * oomag;
 		val2 = g * oomag;
@@ -286,7 +286,7 @@ namespace tut
 		F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
 		LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
 		F32 val = distVec(llcolor3,llcolor3a);
-		ensure("distVec failed ", is_approx_equal(fsqrtf((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val));
+		ensure("distVec failed ", is_approx_equal((F32) sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val));
 		
 		F32 val1 = distVec_squared(llcolor3,llcolor3a);
 		ensure("distVec_squared failed ", is_approx_equal(((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val1));
diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp
index b67346f4e5e6c6cdccd73609385f33dc1c1574b4..20b26faa12b0d08fd6c83d353b424752d5ab460d 100644
--- a/indra/llmath/tests/v3dmath_test.cpp
+++ b/indra/llmath/tests/v3dmath_test.cpp
@@ -30,11 +30,11 @@
 #include "llsd.h"
 #include "../test/lltut.h"
 
-#include "../llquaternion.h"
 #include "../m3math.h"
 #include "../v4math.h"
 #include "../v3dmath.h"
 #include "../v3dmath.h"
+#include "../llquaternion.h"
 
 namespace tut
 {
@@ -403,7 +403,7 @@ namespace tut
 		LLVector3d vec3D(x,y,z);
 		F64 res = (x*x + y*y + z*z) - vec3D.magVecSquared();
 		ensure("1:magVecSquared:Fail ", ((-F_APPROXIMATELY_ZERO <= res)&& (res <=F_APPROXIMATELY_ZERO)));
-		res = fsqrtf(x*x + y*y + z*z) - vec3D.magVec();
+		res = (F32) sqrt(x*x + y*y + z*z) - vec3D.magVec();
 		ensure("2:magVec: Fail ", ((-F_APPROXIMATELY_ZERO <= res)&& (res <=F_APPROXIMATELY_ZERO)));	
 	}
 
diff --git a/indra/llmath/tests/v3math_test.cpp b/indra/llmath/tests/v3math_test.cpp
index e4732bf8615d5369f8053e46a8af7d47a5641df5..df7a77002fc2d9950f55ece6c14a2abe0535d672 100644
--- a/indra/llmath/tests/v3math_test.cpp
+++ b/indra/llmath/tests/v3math_test.cpp
@@ -30,12 +30,12 @@
 #include "../test/lltut.h"
 #include "llsd.h"
 
-#include "../llquaternion.h"
-#include "../llquantize.h"
 #include "../v3dmath.h"
 #include "../m3math.h"
 #include "../v4math.h"
 #include "../v3math.h"
+#include "../llquaternion.h"
+#include "../llquantize.h"
 
 
 namespace tut
@@ -149,7 +149,7 @@ namespace tut
 		F32 x = 2.32f, y = 1.212f, z = -.12f;
 		LLVector3 vec3(x,y,z);		
 		ensure("1:magVecSquared:Fail ", is_approx_equal(vec3.magVecSquared(), (x*x + y*y + z*z)));
-		ensure("2:magVec:Fail ", is_approx_equal(vec3.magVec(), fsqrtf(x*x + y*y + z*z)));
+		ensure("2:magVec:Fail ", is_approx_equal(vec3.magVec(), (F32) sqrt(x*x + y*y + z*z)));
 	}
 
 	template<> template<>
@@ -509,7 +509,7 @@ namespace tut
 		F32 val1,val2;
 		LLVector3 vec3(x1,y1,z1),vec3a(x2,y2,z2);
 		val1 = dist_vec(vec3,vec3a);
-		val2 = fsqrtf((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
+		val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
 		ensure_equals("1:dist_vec: Fail ",val2, val1);
 		val1 = dist_vec_squared(vec3,vec3a);
 		val2 =((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
diff --git a/indra/llmath/tests/v4color_test.cpp b/indra/llmath/tests/v4color_test.cpp
index fbd43625d18c67ee86719f9d9dd372231f0d8a3c..d7eec3c87fd3821aaac250900898f3b004d1957f 100644
--- a/indra/llmath/tests/v4color_test.cpp
+++ b/indra/llmath/tests/v4color_test.cpp
@@ -155,7 +155,7 @@ namespace tut
 		F32 r = 0x20, g = 0xFFFF, b = 0xFF;
 		LLColor4 llcolor4(r,g,b);
 		ensure("magVecSquared:Fail ", is_approx_equal(llcolor4.magVecSquared(), (r*r + g*g + b*b)));
-		ensure("magVec:Fail ", is_approx_equal(llcolor4.magVec(), fsqrtf(r*r + g*g + b*b)));
+		ensure("magVec:Fail ", is_approx_equal(llcolor4.magVec(), (F32) sqrt(r*r + g*g + b*b)));
 	}
 
 	template<> template<>
@@ -164,7 +164,7 @@ namespace tut
 		F32 r = 0x20, g = 0xFFFF, b = 0xFF;
 		LLColor4 llcolor4(r,g,b);
 		F32 vecMag = llcolor4.normVec();
-		F32 mag = fsqrtf(r*r + g*g + b*b);
+		F32 mag = (F32) sqrt(r*r + g*g + b*b);
 		F32 oomag = 1.f / mag;
 		F32 val1 = r * oomag, val2 = g * oomag,	val3 = b * oomag;
 		ensure("1:normVec failed ", (is_approx_equal(val1, llcolor4.mV[0]) && is_approx_equal(val2, llcolor4.mV[1]) && is_approx_equal(val3, llcolor4.mV[2]) && is_approx_equal(vecMag, mag)));
diff --git a/indra/llmath/tests/v4coloru_test.cpp b/indra/llmath/tests/v4coloru_test.cpp
index 6d84ba41ef060b5706440678e2fba1b6d07b271e..128f6f3564334bcea47ff6761e33de2071d91a5e 100644
--- a/indra/llmath/tests/v4coloru_test.cpp
+++ b/indra/llmath/tests/v4coloru_test.cpp
@@ -135,7 +135,7 @@ namespace tut
 		U8 r = 0x12, g = 0xFF, b = 0xAF;
 		LLColor4U llcolor4u(r,g,b);
 		ensure("magVecSquared:Fail ", is_approx_equal(llcolor4u.magVecSquared(), (F32)(r*r + g*g + b*b)));
-		ensure("magVec:Fail ", is_approx_equal(llcolor4u.magVec(), fsqrtf(r*r + g*g + b*b)));
+		ensure("magVec:Fail ", is_approx_equal(llcolor4u.magVec(), (F32) sqrt((F32) (r*r + g*g + b*b))));
 	}
 
 	template<> template<>
diff --git a/indra/llmath/tests/v4math_test.cpp b/indra/llmath/tests/v4math_test.cpp
index b1f934e555288f0fd4c478ad7cdee26dc804ecd8..191ac864dfcf315123a7c7619d4e442478db25aa 100644
--- a/indra/llmath/tests/v4math_test.cpp
+++ b/indra/llmath/tests/v4math_test.cpp
@@ -30,9 +30,9 @@
 #include "../test/lltut.h"
 #include "llsd.h"
 
-#include "../llquaternion.h"
 #include "../m4math.h"
 #include "../v4math.h"
+#include "../llquaternion.h"
 
 namespace tut
 {
@@ -96,7 +96,7 @@ namespace tut
 	{
 		F32 x = 10.f, y = -2.3f, z = -.023f;
 		LLVector4 vec4(x,y,z);
-		ensure("magVec:Fail ", is_approx_equal(vec4.magVec(), fsqrtf(x*x + y*y + z*z)));
+		ensure("magVec:Fail ", is_approx_equal(vec4.magVec(), (F32) sqrt(x*x + y*y + z*z)));
 		ensure("magVecSquared:Fail ", is_approx_equal(vec4.magVecSquared(), (x*x + y*y + z*z)));
 	}
 
@@ -337,7 +337,7 @@ namespace tut
 		F32 val1,val2;
 		LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2);
 		val1 = dist_vec(vec4,vec4a);
-		val2 = fsqrtf((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
+		val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
 		ensure_equals("dist_vec: Fail ",val2, val1);
 		val1 = dist_vec_squared(vec4,vec4a);
 		val2 =((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
diff --git a/indra/llmath/v2math.cpp b/indra/llmath/v2math.cpp
index 0180049b5da32f87602a3f812a7ef406fa973fd9..a0cd6428538b61c197dcf7aa29b4fd57163298b0 100644
--- a/indra/llmath/v2math.cpp
+++ b/indra/llmath/v2math.cpp
@@ -86,7 +86,7 @@ F32	dist_vec(const LLVector2 &a, const LLVector2 &b)
 {
 	F32 x = a.mV[0] - b.mV[0];
 	F32 y = a.mV[1] - b.mV[1];
-	return fsqrtf( x*x + y*y );
+	return (F32) sqrt( x*x + y*y );
 }
 
 F32	dist_vec_squared(const LLVector2 &a, const LLVector2 &b)
@@ -109,3 +109,18 @@ LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u)
 		a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
 		a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u );
 }
+
+LLSD LLVector2::getValue() const
+{
+	LLSD ret;
+	ret[0] = mV[0];
+	ret[1] = mV[1];
+	return ret;
+}
+
+void LLVector2::setValue(LLSD& sd)
+{
+	mV[0] = (F32) sd[0].asReal();
+	mV[1] = (F32) sd[1].asReal();
+}
+
diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h
index f50a5e6633721da4ac3af759e7e6ff2f36660b50..8d5db96f5e295633531865c8254960526b970037 100644
--- a/indra/llmath/v2math.h
+++ b/indra/llmath/v2math.h
@@ -60,6 +60,9 @@ class LLVector2
 		void	set(const LLVector2 &vec);	// Sets LLVector2 to vec
 		void	set(const F32 *vec);			// Sets LLVector2 to vec
 
+		LLSD	getValue() const;
+		void	setValue(LLSD& sd);
+
 		void	setVec(F32 x, F32 y);	        // deprecated
 		void	setVec(const LLVector2 &vec);	// deprecated
 		void	setVec(const F32 *vec);			// deprecated
@@ -216,7 +219,7 @@ inline void	LLVector2::setVec(const F32 *vec)
 
 inline F32 LLVector2::length(void) const
 {
-	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+	return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
 }
 
 inline F32 LLVector2::lengthSquared(void) const
@@ -226,7 +229,7 @@ inline F32 LLVector2::lengthSquared(void) const
 
 inline F32		LLVector2::normalize(void)
 {
-	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+	F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
 	F32 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
@@ -253,7 +256,7 @@ inline bool LLVector2::isFinite() const
 // deprecated
 inline F32		LLVector2::magVec(void) const
 {
-	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+	return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
 }
 
 // deprecated
@@ -265,7 +268,7 @@ inline F32		LLVector2::magVecSquared(void) const
 // deprecated
 inline F32		LLVector2::normVec(void)
 {
-	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+	F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
 	F32 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h
index 327e452bf70caa95386e14b1f816dd80ac1f20ea..56cb2ae73e57be7f0cdce9c1065479a965819cf6 100644
--- a/indra/llmath/v3color.h
+++ b/indra/llmath/v3color.h
@@ -278,7 +278,7 @@ inline F32		LLColor3::brightness(void) const
 
 inline F32		LLColor3::length(void) const
 {
-	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 }
 
 inline F32		LLColor3::lengthSquared(void) const
@@ -288,7 +288,7 @@ inline F32		LLColor3::lengthSquared(void) const
 
 inline F32		LLColor3::normalize(void)
 {
-	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 	F32 oomag;
 
 	if (mag)
@@ -304,7 +304,7 @@ inline F32		LLColor3::normalize(void)
 // deprecated
 inline F32		LLColor3::magVec(void) const
 {
-	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 }
 
 // deprecated
@@ -316,7 +316,7 @@ inline F32		LLColor3::magVecSquared(void) const
 // deprecated
 inline F32		LLColor3::normVec(void)
 {
-	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 	F32 oomag;
 
 	if (mag)
@@ -438,7 +438,7 @@ inline F32		distVec(const LLColor3 &a, const LLColor3 &b)
 	F32 x = a.mV[0] - b.mV[0];
 	F32 y = a.mV[1] - b.mV[1];
 	F32 z = a.mV[2] - b.mV[2];
-	return fsqrtf( x*x + y*y + z*z );
+	return (F32) sqrt( x*x + y*y + z*z );
 }
 
 inline F32		distVec_squared(const LLColor3 &a, const LLColor3 &b)
diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h
index 664c986ad0e2c8df0fc0dc1057667e6fe1948a62..578dcdc8ea5e1d85d425cc6947668b0e530b7a66 100644
--- a/indra/llmath/v3dmath.h
+++ b/indra/llmath/v3dmath.h
@@ -234,7 +234,7 @@ inline const LLVector3d&	LLVector3d::setVec(const F64 *vec)
 
 inline F64 LLVector3d::normVec(void)
 {
-	F64 mag = fsqrtf(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+	F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
 	F64 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
@@ -256,7 +256,7 @@ inline F64 LLVector3d::normVec(void)
 
 inline F64 LLVector3d::normalize(void)
 {
-	F64 mag = fsqrtf(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+	F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
 	F64 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
@@ -280,7 +280,7 @@ inline F64 LLVector3d::normalize(void)
 
 inline F64	LLVector3d::magVec(void) const
 {
-	return fsqrtf(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+	return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
 }
 
 inline F64	LLVector3d::magVecSquared(void) const
@@ -290,7 +290,7 @@ inline F64	LLVector3d::magVecSquared(void) const
 
 inline F64	LLVector3d::length(void) const
 {
-	return fsqrtf(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+	return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
 }
 
 inline F64	LLVector3d::lengthSquared(void) const
@@ -400,7 +400,7 @@ inline F64	dist_vec(const LLVector3d &a, const LLVector3d &b)
 	F64 x = a.mdV[0] - b.mdV[0];
 	F64 y = a.mdV[1] - b.mdV[1];
 	F64 z = a.mdV[2] - b.mdV[2];
-	return fsqrtf( x*x + y*y + z*z );
+	return (F32) sqrt( x*x + y*y + z*z );
 }
 
 inline F64	dist_vec_squared(const LLVector3d &a, const LLVector3d &b)
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
index 18b15e08c4c89b3f62ce36d3cb4c9621f0110d72..e7107dee16420cc5fe8696872902a24c3cb3175a 100644
--- a/indra/llmath/v3math.cpp
+++ b/indra/llmath/v3math.cpp
@@ -206,6 +206,28 @@ const LLVector3&	LLVector3::rotVec(const LLQuaternion &q)
 	return *this;
 }
 
+const LLVector3& LLVector3::transVec(const LLMatrix4& mat)
+{
+	setVec(
+			mV[VX] * mat.mMatrix[VX][VX] + 
+			mV[VY] * mat.mMatrix[VX][VY] + 
+			mV[VZ] * mat.mMatrix[VX][VZ] +
+			mat.mMatrix[VX][VW],
+			 
+			mV[VX] * mat.mMatrix[VY][VX] + 
+			mV[VY] * mat.mMatrix[VY][VY] + 
+			mV[VZ] * mat.mMatrix[VY][VZ] +
+			mat.mMatrix[VY][VW],
+
+			mV[VX] * mat.mMatrix[VZ][VX] + 
+			mV[VY] * mat.mMatrix[VZ][VY] + 
+			mV[VZ] * mat.mMatrix[VZ][VZ] +
+			mat.mMatrix[VZ][VW]);
+
+	return *this;
+}
+
+
 const LLVector3&	LLVector3::rotVec(F32 angle, const LLVector3 &vec)
 {
 	if ( !vec.isExactlyZero() && angle )
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
index 4b3efe739407f3e2b4e51a76a8d616de46f6b42a..0432aeba4cca1a9bba7cfb77ce16d7042aa6bc51 100644
--- a/indra/llmath/v3math.h
+++ b/indra/llmath/v3math.h
@@ -34,6 +34,7 @@
 class LLVector2;
 class LLVector4;
 class LLMatrix3;
+class LLMatrix4;
 class LLVector3d;
 class LLQuaternion;
 
@@ -110,6 +111,7 @@ class LLVector3
 		const LLVector3&	rotVec(F32 angle, F32 x, F32 y, F32 z);		// Rotates about x,y,z by angle radians
 		const LLVector3&	rotVec(const LLMatrix3 &mat);				// Rotates by LLMatrix4 mat
 		const LLVector3&	rotVec(const LLQuaternion &q);				// Rotates by LLQuaternion q
+		const LLVector3&	transVec(const LLMatrix4& mat);				// Transforms by LLMatrix4 mat (mat * v)
 
 		const LLVector3&	scaleVec(const LLVector3& vec);				// scales per component by vec
 		LLVector3			scaledVec(const LLVector3& vec) const;			// get a copy of this vector scaled by vec
@@ -277,7 +279,7 @@ inline void	LLVector3::setVec(const F32 *vec)
 
 inline F32 LLVector3::normalize(void)
 {
-	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 	F32 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
@@ -300,7 +302,7 @@ inline F32 LLVector3::normalize(void)
 // deprecated
 inline F32 LLVector3::normVec(void)
 {
-	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 	F32 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
@@ -324,7 +326,7 @@ inline F32 LLVector3::normVec(void)
 
 inline F32	LLVector3::length(void) const
 {
-	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 }
 
 inline F32	LLVector3::lengthSquared(void) const
@@ -334,7 +336,7 @@ inline F32	LLVector3::lengthSquared(void) const
 
 inline F32	LLVector3::magVec(void) const
 {
-	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
 }
 
 inline F32	LLVector3::magVecSquared(void) const
@@ -468,7 +470,7 @@ inline F32	dist_vec(const LLVector3 &a, const LLVector3 &b)
 	F32 x = a.mV[0] - b.mV[0];
 	F32 y = a.mV[1] - b.mV[1];
 	F32 z = a.mV[2] - b.mV[2];
-	return fsqrtf( x*x + y*y + z*z );
+	return (F32) sqrt( x*x + y*y + z*z );
 }
 
 inline F32	dist_vec_squared(const LLVector3 &a, const LLVector3 &b)
@@ -537,6 +539,21 @@ inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos)
 	}
 }
 
+inline void update_min_max(LLVector3& min, LLVector3& max, const F32* pos)
+{
+	for (U32 i = 0; i < 3; i++)
+	{
+		if (min.mV[i] > pos[i])
+		{
+			min.mV[i] = pos[i];
+		}
+		if (max.mV[i] < pos[i])
+		{
+			max.mV[i] = pos[i];
+		}
+	}
+}
+
 inline F32 angle_between(const LLVector3& a, const LLVector3& b)
 {
 	LLVector3 an = a;
diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h
index 60d24e2e11064545c11eb897c5b036df97c39ea7..b047f86e6ef0c9fbcefaa3a7dfd3b4aec33ef402 100644
--- a/indra/llmath/v4color.h
+++ b/indra/llmath/v4color.h
@@ -108,6 +108,7 @@ class LLColor4
 	
 	    const LLColor4& operator=(const LLColor3 &a);	// Assigns vec3 to vec4 and returns vec4
 		
+		bool operator<(const LLColor4& rhs) const;
 		friend std::ostream&	 operator<<(std::ostream& s, const LLColor4 &a);		// Print a
 		friend LLColor4 operator+(const LLColor4 &a, const LLColor4 &b);	// Return vector a + b
 		friend LLColor4 operator-(const LLColor4 &a, const LLColor4 &b);	// Return vector a minus b
@@ -385,7 +386,7 @@ inline const LLColor4&	LLColor4::setAlpha(F32 a)
 
 inline F32		LLColor4::length(void) const
 {
-	return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 }
 
 inline F32		LLColor4::lengthSquared(void) const
@@ -395,7 +396,7 @@ inline F32		LLColor4::lengthSquared(void) const
 
 inline F32		LLColor4::normalize(void)
 {
-	F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 	F32 oomag;
 
 	if (mag)
@@ -411,7 +412,7 @@ inline F32		LLColor4::normalize(void)
 // deprecated
 inline F32		LLColor4::magVec(void) const
 {
-	return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 }
 
 // deprecated
@@ -423,7 +424,7 @@ inline F32		LLColor4::magVecSquared(void) const
 // deprecated
 inline F32		LLColor4::normVec(void)
 {
-	F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 	F32 oomag;
 
 	if (mag)
@@ -589,6 +590,23 @@ inline LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u)
 		a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
 }
 
+inline bool LLColor4::operator<(const LLColor4& rhs) const
+{
+	if (mV[0] != rhs.mV[0])
+	{
+		return mV[0] < rhs.mV[0];
+	}
+	if (mV[1] != rhs.mV[1])
+	{
+		return mV[1] < rhs.mV[1];
+	}
+	if (mV[2] != rhs.mV[2])
+	{
+		return mV[2] < rhs.mV[2];
+	}
+
+	return mV[3] < rhs.mV[3];
+}
 
 void LLColor4::clamp()
 {
diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h
index 7471aebe029253b8162b12290204e815f934c471..12da7e2dd75c3c4ddcfe64a6bf0011dc7aa67d5c 100644
--- a/indra/llmath/v4coloru.h
+++ b/indra/llmath/v4coloru.h
@@ -294,7 +294,7 @@ inline const LLColor4U&	LLColor4U::setAlpha(U8 a)
 
 inline F32		LLColor4U::length(void) const
 {
-	return fsqrtf( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
+	return (F32) sqrt( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
 }
 
 inline F32		LLColor4U::lengthSquared(void) const
@@ -305,7 +305,7 @@ inline F32		LLColor4U::lengthSquared(void) const
 // deprecated
 inline F32		LLColor4U::magVec(void) const
 {
-	return fsqrtf( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
+	return (F32) sqrt( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
 }
 
 // deprecated
diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h
index e7028626f986bdb404cd1454b1b63e256351ee5d..623c8b200353bb60bd5f052213a2b3a8f86cc7e8 100644
--- a/indra/llmath/v4math.h
+++ b/indra/llmath/v4math.h
@@ -315,7 +315,7 @@ inline void	LLVector4::setVec(const F32 *vec)
 
 inline F32		LLVector4::length(void) const
 {
-	return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 }
 
 inline F32		LLVector4::lengthSquared(void) const
@@ -325,7 +325,7 @@ inline F32		LLVector4::lengthSquared(void) const
 
 inline F32		LLVector4::magVec(void) const
 {
-	return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 }
 
 inline F32		LLVector4::magVecSquared(void) const
@@ -457,7 +457,7 @@ inline LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u)
 
 inline F32		LLVector4::normalize(void)
 {
-	F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 	F32 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
@@ -480,7 +480,7 @@ inline F32		LLVector4::normalize(void)
 // deprecated
 inline F32		LLVector4::normVec(void)
 {
-	F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
 	F32 oomag;
 
 	if (mag > FP_MAG_THRESHOLD)
diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h
index 5159c1cbfeaaac7e653ad33d3ab7974efa8547c3..1b50749b3e07d5646d98d9a427e875c4e9f4c09a 100644
--- a/indra/llmath/xform.h
+++ b/indra/llmath/xform.h
@@ -32,11 +32,11 @@
 
 const F32 MAX_OBJECT_Z 		= 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f
 const F32 MIN_OBJECT_Z 		= -256.f;
-const F32 DEFAULT_MAX_PRIM_SCALE = 10.f;
+const F32 DEFAULT_MAX_PRIM_SCALE = 64.f;
+const F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = 10.f;
 const F32 MIN_PRIM_SCALE = 0.01f;
 const F32 MAX_PRIM_SCALE = 65536.f;	// something very high but not near FLT_MAX
 
-
 class LLXform
 {
 protected:
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
index 69d092de764647a3f4f5a3db49854759b4030c05..31cdb1219bd3d1235c915c5e0c5dcafc784f6d92 100644
--- a/indra/llmessage/llassetstorage.cpp
+++ b/indra/llmessage/llassetstorage.cpp
@@ -561,7 +561,7 @@ void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType at
 			tpvf.setAsset(uuid, atype);
 			tpvf.setCallback(downloadCompleteCallback, req);
 
-			llinfos << "Starting transfer for " << uuid << llendl;
+			//llinfos << "Starting transfer for " << uuid << llendl;
 			LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
 			ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
 		}
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index 9b3b24c3123c62406367a06c2d98054eda7c1284..7c8b7e3584b6818309a3c5936dc768a3cdb43278 100644
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -49,6 +49,7 @@
 #include "llstl.h"
 #include "llsdserialize.h"
 #include "llthread.h"
+#include "lltimer.h"
 
 //////////////////////////////////////////////////////////////////////////////
 /*
@@ -84,6 +85,26 @@ std::vector<LLMutex*> LLCurl::sSSLMutex;
 std::string LLCurl::sCAPath;
 std::string LLCurl::sCAFile;
 
+void check_curl_code(CURLcode code)
+{
+	if (code != CURLE_OK)
+	{
+		// linux appears to throw a curl error once per session for a bad initialization
+		// at a pretty random time (when enabling cookies).
+		llinfos << "curl error detected: " << curl_easy_strerror(code) << llendl;
+	}
+}
+
+void check_curl_multi_code(CURLMcode code) 
+{
+	if (code != CURLM_OK)
+	{
+		// linux appears to throw a curl error once per session for a bad initialization
+		// at a pretty random time (when enabling cookies).
+		llinfos << "curl multi error detected: " << curl_multi_strerror(code) << llendl;
+	}
+}
+
 //static
 void LLCurl::setCAPath(const std::string& path)
 {
@@ -234,7 +255,12 @@ class LLCurl::Easy
 	
 	void resetState();
 
+	static CURL* allocEasyHandle();
+	static void releaseEasyHandle(CURL* handle);
+
 private:	
+	friend class LLCurl;
+
 	CURL*				mCurlEasyHandle;
 	struct curl_slist*	mHeaders;
 	
@@ -249,8 +275,62 @@ class LLCurl::Easy
 	std::vector<char*>	mStrings;
 	
 	ResponderPtr		mResponder;
+
+	static std::set<CURL*> sFreeHandles;
+	static std::set<CURL*> sActiveHandles;
+	static LLMutex* sHandleMutex;
 };
 
+std::set<CURL*> LLCurl::Easy::sFreeHandles;
+std::set<CURL*> LLCurl::Easy::sActiveHandles;
+LLMutex* LLCurl::Easy::sHandleMutex = NULL;
+
+
+//static
+CURL* LLCurl::Easy::allocEasyHandle()
+{
+	CURL* ret = NULL;
+	LLMutexLock lock(sHandleMutex);
+	if (sFreeHandles.empty())
+	{
+		ret = curl_easy_init();
+	}
+	else
+	{
+		ret = *(sFreeHandles.begin());
+		sFreeHandles.erase(ret);
+		curl_easy_reset(ret);
+	}
+
+	if (ret)
+	{
+		sActiveHandles.insert(ret);
+	}
+
+	return ret;
+}
+
+//static
+void LLCurl::Easy::releaseEasyHandle(CURL* handle)
+{
+	if (!handle)
+	{
+		llerrs << "handle cannot be NULL!" << llendl;
+	}
+
+	LLMutexLock lock(sHandleMutex);
+	
+	if (sActiveHandles.find(handle) != sActiveHandles.end())
+	{
+		sActiveHandles.erase(handle);
+		sFreeHandles.insert(handle);
+	}
+	else
+	{
+		llerrs << "Invalid handle." << llendl;
+	}
+}
+
 LLCurl::Easy::Easy()
 	: mHeaders(NULL),
 	  mCurlEasyHandle(NULL)
@@ -261,25 +341,28 @@ LLCurl::Easy::Easy()
 LLCurl::Easy* LLCurl::Easy::getEasy()
 {
 	Easy* easy = new Easy();
-	easy->mCurlEasyHandle = curl_easy_init();
+	easy->mCurlEasyHandle = allocEasyHandle(); 
+	
 	if (!easy->mCurlEasyHandle)
 	{
 		// this can happen if we have too many open files (fails in c-ares/ares_init.c)
-		llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
+		llwarns << "allocEasyHandle() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
 		delete easy;
 		return NULL;
 	}
 	
-	// set no DMS caching as default for all easy handles. This prevents them adopting a
+	// set no DNS caching as default for all easy handles. This prevents them adopting a
 	// multi handles cache if they are added to one.
-	curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
+	CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
+	check_curl_code(result);
+	
 	++gCurlEasyCount;
 	return easy;
 }
 
 LLCurl::Easy::~Easy()
 {
-	curl_easy_cleanup(mCurlEasyHandle);
+	releaseEasyHandle(mCurlEasyHandle);
 	--gCurlEasyCount;
 	curl_slist_free_all(mHeaders);
 	for_each(mStrings.begin(), mStrings.end(), DeletePointerArray());
@@ -338,9 +421,9 @@ void LLCurl::Easy::setHeaders()
 
 void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info)
 {
-	curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload);
-	curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime);
-	curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload);
+	check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload));
+	check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime));
+	check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload));
 }
 
 U32 LLCurl::Easy::report(CURLcode code)
@@ -350,13 +433,14 @@ U32 LLCurl::Easy::report(CURLcode code)
 	
 	if (code == CURLE_OK)
 	{
-		curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
+		check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode));
 		//*TODO: get reason from first line of mHeaderOutput
 	}
 	else
 	{
 		responseCode = 499;
 		responseReason = strerror(code) + " : " + mErrorBuffer;
+		setopt(CURLOPT_FRESH_CONNECT, TRUE);
 	}
 
 	if (mResponder)
@@ -372,17 +456,20 @@ U32 LLCurl::Easy::report(CURLcode code)
 // Note: these all assume the caller tracks the value (i.e. keeps it persistant)
 void LLCurl::Easy::setopt(CURLoption option, S32 value)
 {
-	curl_easy_setopt(mCurlEasyHandle, option, value);
+	CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value);
+	check_curl_code(result);
 }
 
 void LLCurl::Easy::setopt(CURLoption option, void* value)
 {
-	curl_easy_setopt(mCurlEasyHandle, option, value);
+	CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value);
+	check_curl_code(result);
 }
 
 void LLCurl::Easy::setopt(CURLoption option, char* value)
 {
-	curl_easy_setopt(mCurlEasyHandle, option, value);
+	CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value);
+	check_curl_code(result);
 }
 
 // Note: this copies the string so that the caller does not have to keep it around
@@ -391,7 +478,8 @@ void LLCurl::Easy::setoptString(CURLoption option, const std::string& value)
 	char* tstring = new char[value.length()+1];
 	strcpy(tstring, value.c_str());
 	mStrings.push_back(tstring);
-	curl_easy_setopt(mCurlEasyHandle, option, tstring);
+	CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, tstring);
+	check_curl_code(result);
 }
 
 void LLCurl::Easy::slist_append(const char* str)
@@ -443,7 +531,7 @@ void LLCurl::Easy::prepRequest(const std::string& url,
 	
 	if (post) setoptString(CURLOPT_ENCODING, "");
 
-//	setopt(CURLOPT_VERBOSE, 1); // usefull for debugging
+	//setopt(CURLOPT_VERBOSE, 1); // usefull for debugging
 	setopt(CURLOPT_NOSIGNAL, 1);
 
 	mOutput.reset(new LLBufferArray);
@@ -467,6 +555,9 @@ void LLCurl::Easy::prepRequest(const std::string& url,
 	setCA();
 
 	setopt(CURLOPT_SSL_VERIFYPEER, true);
+	
+	//don't verify host name so urls with scrubbed host names will work (improves DNS performance)
+	setopt(CURLOPT_SSL_VERIFYHOST, 0);
 	setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT);
 
 	setoptString(CURLOPT_URL, url);
@@ -532,6 +623,7 @@ LLCurl::Multi::Multi()
 		llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
 		mCurlMultiHandle = curl_multi_init();
 	}
+	
 	llassert_always(mCurlMultiHandle);
 	++gCurlMultiCount;
 }
@@ -543,7 +635,7 @@ LLCurl::Multi::~Multi()
 		iter != mEasyActiveList.end(); ++iter)
 	{
 		Easy* easy = *iter;
-		curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle());
+		check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()));
 		delete easy;
 	}
 	mEasyActiveList.clear();
@@ -553,7 +645,7 @@ LLCurl::Multi::~Multi()
 	for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer());	
 	mEasyFreeList.clear();
 
-	curl_multi_cleanup(mCurlMultiHandle);
+	check_curl_multi_code(curl_multi_cleanup(mCurlMultiHandle));
 	--gCurlMultiCount;
 }
 
@@ -574,8 +666,10 @@ S32 LLCurl::Multi::perform()
 		CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
 		if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
 		{
+			check_curl_multi_code(code);
 			break;
 		}
+	
 	}
 	mQueued = q;
 	return q;
@@ -642,11 +736,12 @@ LLCurl::Easy* LLCurl::Multi::allocEasy()
 bool LLCurl::Multi::addEasy(Easy* easy)
 {
 	CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle());
-	if (mcode != CURLM_OK)
-	{
-		llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl;
-		return false;
-	}
+	check_curl_multi_code(mcode);
+	//if (mcode != CURLM_OK)
+	//{
+	//	llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl;
+	//	return false;
+	//}
 	return true;
 }
 
@@ -667,7 +762,7 @@ void LLCurl::Multi::easyFree(Easy* easy)
 
 void LLCurl::Multi::removeEasy(Easy* easy)
 {
-	curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle());
+	check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()));
 	easyFree(easy);
 }
 
@@ -686,6 +781,7 @@ LLCurlRequest::LLCurlRequest() :
 	mActiveRequestCount(0)
 {
 	mThreadID = LLThread::currentID();
+	mProcessing = FALSE;
 }
 
 LLCurlRequest::~LLCurlRequest()
@@ -720,6 +816,11 @@ LLCurl::Easy* LLCurlRequest::allocEasy()
 bool LLCurlRequest::addEasy(LLCurl::Easy* easy)
 {
 	llassert_always(mActiveMulti);
+	
+	if (mProcessing)
+	{
+		llerrs << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << llendl;
+	}
 	bool res = mActiveMulti->addEasy(easy);
 	return res;
 }
@@ -777,12 +878,41 @@ bool LLCurlRequest::post(const std::string& url,
 	bool res = addEasy(easy);
 	return res;
 }
+
+bool LLCurlRequest::post(const std::string& url,
+						 const headers_t& headers,
+						 const std::string& data,
+						 LLCurl::ResponderPtr responder)
+{
+	LLCurl::Easy* easy = allocEasy();
+	if (!easy)
+	{
+		return false;
+	}
+	easy->prepRequest(url, headers, responder);
+
+	easy->getInput().write(data.data(), data.size());
+	S32 bytes = easy->getInput().str().length();
 	
+	easy->setopt(CURLOPT_POST, 1);
+	easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL);
+	easy->setopt(CURLOPT_POSTFIELDSIZE, bytes);
+
+	easy->slist_append("Content-Type: application/octet-stream");
+	easy->setHeaders();
+
+	lldebugs << "POSTING: " << bytes << " bytes." << llendl;
+	bool res = addEasy(easy);
+	return res;
+}
+
 // Note: call once per frame
 S32 LLCurlRequest::process()
 {
 	llassert_always(mThreadID == LLThread::currentID());
 	S32 res = 0;
+
+	mProcessing = TRUE;
 	for (curlmulti_set_t::iterator iter = mMultiSet.begin();
 		 iter != mMultiSet.end(); )
 	{
@@ -796,6 +926,7 @@ S32 LLCurlRequest::process()
 			delete multi;
 		}
 	}
+	mProcessing = FALSE;
 	return res;
 }
 
@@ -1025,8 +1156,12 @@ void LLCurl::initClass()
 	// Do not change this "unless you are familiar with and mean to control 
 	// internal operations of libcurl"
 	// - http://curl.haxx.se/libcurl/c/curl_global_init.html
-	curl_global_init(CURL_GLOBAL_ALL);
+	CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
+
+	check_curl_code(code);
 	
+	Easy::sHandleMutex = new LLMutex(NULL);
+
 #if SAFE_SSL
 	S32 mutex_count = CRYPTO_num_locks();
 	for (S32 i=0; i<mutex_count; i++)
@@ -1044,7 +1179,19 @@ void LLCurl::cleanupClass()
 	CRYPTO_set_locking_callback(NULL);
 	for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer());
 #endif
-	curl_global_cleanup();
+
+	delete Easy::sHandleMutex;
+	Easy::sHandleMutex = NULL;
+
+	for (std::set<CURL*>::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter)
+	{
+		CURL* curl = *iter;
+		curl_easy_cleanup(curl);
+	}
+
+	Easy::sFreeHandles.clear();
+
+	llassert(Easy::sActiveHandles.empty());
 }
 
 const unsigned int LLCurl::MAX_REDIRECTS = 5;
diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h
index 64dadd6640c48ad5fb724d0a74935c9b41e69d22..4ce3fa10780b4c7021432280acd548531e9fc1f9 100644
--- a/indra/llmessage/llcurl.h
+++ b/indra/llmessage/llcurl.h
@@ -202,6 +202,8 @@ class LLCurlRequest
 	void get(const std::string& url, LLCurl::ResponderPtr responder);
 	bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
 	bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder);
+	bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder);
+	
 	S32  process();
 	S32  getQueued();
 
@@ -215,6 +217,7 @@ class LLCurlRequest
 	curlmulti_set_t mMultiSet;
 	LLCurl::Multi* mActiveMulti;
 	S32 mActiveRequestCount;
+	BOOL mProcessing;
 	U32 mThreadID; // debug
 };
 
diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp
index 9ea2ff4153859ae1369809510c3be1229c3a51f0..5a38b7fd9f41836a0b5216058d3f402597829957 100644
--- a/indra/llmessage/llhttpassetstorage.cpp
+++ b/indra/llmessage/llhttpassetstorage.cpp
@@ -174,8 +174,8 @@ LLSD LLHTTPAssetRequest::getFullDetails() const
 		double curl_total_time = -1.0f;
 		double curl_size_upload = -1.0f;
 		double curl_size_download = -1.0f;
-		long curl_content_length_upload = -1;
-		long curl_content_length_download = -1;
+		double curl_content_length_upload = -1.0f;
+		double curl_content_length_download = -1.0f;
 		long curl_request_size = -1;
 		const char* curl_content_type = NULL;
 
@@ -194,8 +194,8 @@ LLSD LLHTTPAssetRequest::getFullDetails() const
 		sd["curl_total_time"] = curl_total_time;
 		sd["curl_size_upload"]   = curl_size_upload;
 		sd["curl_size_download"] = curl_size_download;
-		sd["curl_content_length_upload"]   = (int) curl_content_length_upload;
-		sd["curl_content_length_download"] = (int) curl_content_length_download;
+		sd["curl_content_length_upload"]   =  curl_content_length_upload;
+		sd["curl_content_length_download"] =  curl_content_length_download;
 		sd["curl_request_size"] = (int) curl_request_size;
 		if (curl_content_type)
 		{
diff --git a/indra/llmessage/llsdmessagebuilder.cpp b/indra/llmessage/llsdmessagebuilder.cpp
index 42c179782f6f7b90e5de54a327c45d4cc4f117b9..2698a271ee972e4c58266b892f840e3e7bebd4b0 100644
--- a/indra/llmessage/llsdmessagebuilder.cpp
+++ b/indra/llmessage/llsdmessagebuilder.cpp
@@ -29,6 +29,7 @@
 #include "llsdmessagebuilder.h"
 
 #include "llmessagetemplate.h"
+#include "llmath.h"
 #include "llquaternion.h"
 #include "llsdutil.h"
 #include "llsdutil_math.h"
diff --git a/indra/llmessage/lltemplatemessagebuilder.cpp b/indra/llmessage/lltemplatemessagebuilder.cpp
index 6611d704e60a25593a6e8999eba34c4ba0aaef5f..9e8eb484606c215d8f0fe689db2f1de66dc95da2 100644
--- a/indra/llmessage/lltemplatemessagebuilder.cpp
+++ b/indra/llmessage/lltemplatemessagebuilder.cpp
@@ -29,6 +29,7 @@
 #include "lltemplatemessagebuilder.h"
 
 #include "llmessagetemplate.h"
+#include "llmath.h"
 #include "llquaternion.h"
 #include "u64.h"
 #include "v3dmath.h"
diff --git a/indra/llmessage/lltemplatemessagereader.cpp b/indra/llmessage/lltemplatemessagereader.cpp
index 3bfcd58c6936535816519782621c8a7dc4c1c8bd..f470e1b2a52fdf955a22203370ee06e0ea4581c2 100644
--- a/indra/llmessage/lltemplatemessagereader.cpp
+++ b/indra/llmessage/lltemplatemessagereader.cpp
@@ -30,6 +30,7 @@
 #include "llfasttimer.h"
 #include "llmessagebuilder.h"
 #include "llmessagetemplate.h"
+#include "llmath.h"
 #include "llquaternion.h"
 #include "message.h"
 #include "u64.h"
diff --git a/indra/llmessage/lltransfermanager.cpp b/indra/llmessage/lltransfermanager.cpp
index 754eb99cbd7a07d251cc278102de45ad2e1f14ef..034680caf8c042babdcb8c4f11a796c6b6fb70ac 100644
--- a/indra/llmessage/lltransfermanager.cpp
+++ b/indra/llmessage/lltransfermanager.cpp
@@ -338,7 +338,7 @@ void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
 		}
 	}
 
-	llinfos << "Receiving " << transfer_id << ", size " << size << " bytes" << llendl;
+	//llinfos << "Receiving " << transfer_id << ", size " << size << " bytes" << llendl;
 	ttp->setSize(size);
 	ttp->setGotInfo(TRUE);
 
diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp
index 7e57841580bc0466dcefc61ce0e2b9375edd2c67..8537773a3f70ab739beaa66083751bb6a784066a 100644
--- a/indra/llmessage/lltransfersourceasset.cpp
+++ b/indra/llmessage/lltransfersourceasset.cpp
@@ -251,3 +251,4 @@ BOOL LLTransferSourceParamsAsset::unpackParams(LLDataPacker &dp)
 
 	return TRUE;
 }
+
diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp
index 9998a1b8bb0ba9966fba0e00dc6f2ad35a2322fe..0f2c0693031351ed7e34fcec041d1de2be90ae86 100644
--- a/indra/llmessage/tests/llsdmessage_test.cpp
+++ b/indra/llmessage/tests/llsdmessage_test.cpp
@@ -61,6 +61,7 @@ namespace tut
         llsdmessage_data():
             httpPump(pumps.obtain("LLHTTPClient"))
         {
+            LLCurl::initClass();
             LLSDMessage::link();
         }
     };
diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt
index 1dc05e0b20ba95a29fd4377f610a763f9eefa67f..2f28673c07531dbf7638294ac8add55194a9f854 100644
--- a/indra/llplugin/CMakeLists.txt
+++ b/indra/llplugin/CMakeLists.txt
@@ -66,21 +66,20 @@ add_library (llplugin ${llplugin_SOURCE_FILES})
 
 add_subdirectory(slplugin)
 
+# Add tests
 if (LL_TESTS)
-  # Add tests
-  include(LLAddBuildTest)
+	include(LLAddBuildTest)
+	# UNIT TESTS
+	SET(llplugin_TEST_SOURCE_FILES
+	  llplugincookiestore.cpp
+	  )
 
-  # UNIT TESTS
-  SET(llplugin_TEST_SOURCE_FILES
-      llplugincookiestore.cpp
-      )
+	# llplugincookiestore has a dependency on curl, so we need to link the curl library into the test.
+	set_source_files_properties(
+	  llplugincookiestore.cpp
+	  PROPERTIES
+		LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}"
+	  )
 
-  # llplugincookiestore has a dependency on curl, so we need to link the curl library into the test.
-  set_source_files_properties(
-      llplugincookiestore.cpp
-      PROPERTIES
-      LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}"
-      )
-
-  LL_ADD_PROJECT_UNIT_TESTS(llplugin "${llplugin_TEST_SOURCE_FILES}")
+	LL_ADD_PROJECT_UNIT_TESTS(llplugin "${llplugin_TEST_SOURCE_FILES}")
 endif (LL_TESTS)
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index f4d21308b3fdfe7915993b19872253abeccf0a05..97e1ebde477587fb8398987fd92778dfafb47edf 100644
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -13,11 +13,14 @@ include_directories(
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
+    ${LIBS_PREBUILT_DIR}/include/collada
+    ${LIBS_PREBUILT_DIR}/include/collada/1.4
     )
 
 set(llprimitive_SOURCE_FILES
     llmaterialtable.cpp
     llmediaentry.cpp
+    llmodel.cpp
     llprimitive.cpp
     llprimtexturelist.cpp
     lltextureanim.cpp
@@ -34,6 +37,7 @@ set(llprimitive_HEADER_FILES
     legacy_object_types.h
     llmaterialtable.h
     llmediaentry.h
+    llmodel.h
     llprimitive.h
     llprimtexturelist.h
     lltextureanim.h
@@ -53,11 +57,11 @@ list(APPEND llprimitive_SOURCE_FILES ${llprimitive_HEADER_FILES})
 
 add_library (llprimitive ${llprimitive_SOURCE_FILES})
 
-if(LL_TESTS)
-  #add unit tests
-  INCLUDE(LLAddBuildTest)
-  SET(llprimitive_TEST_SOURCE_FILES
-      llmediaentry.cpp
-      )
-  LL_ADD_PROJECT_UNIT_TESTS(llprimitive "${llprimitive_TEST_SOURCE_FILES}")
-endif(LL_TESTS)
+#add unit tests
+if (LL_TESTS)
+	INCLUDE(LLAddBuildTest)
+	SET(llprimitive_TEST_SOURCE_FILES
+	  llmediaentry.cpp
+	  )
+	LL_ADD_PROJECT_UNIT_TESTS(llprimitive "${llprimitive_TEST_SOURCE_FILES}")
+endif (LL_TESTS)
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..794cdb83d592d82531ea6a4babc48cb3d0192b5d
--- /dev/null
+++ b/indra/llprimitive/llmodel.cpp
@@ -0,0 +1,2292 @@
+/** 
+ * @file llmodel.cpp
+ * @brief Model handling implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmodel.h"
+#include "llconvexdecomposition.h"
+#include "llsdserialize.h"
+#include "llvector4a.h"
+
+#include "dae.h"
+#include "dae/daeErrorHandler.h"
+#include "dom/domConstants.h"
+#include "dom/domMesh.h"
+
+#ifdef LL_STANDALONE
+# include <zlib.h>
+#else
+# include "zlib/zlib.h"
+#endif
+
+
+
+std::string model_names[] =
+{
+	"lowest_lod",
+	"low_lod",
+	"medium_lod",
+	"high_lod",
+	"physics_shape"
+};
+
+const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
+
+LLModel::LLModel(LLVolumeParams& params, F32 detail)
+	: LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0)
+	, mPelvisOffset( 0.0f ), mStatus(NO_ERRORS)
+{
+	mDecompID = -1;
+	mLocalID = -1;
+}
+
+LLModel::~LLModel()
+{
+	if (mDecompID >= 0)
+	{
+		LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID);
+	}
+}
+
+void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Array& inputs, U32 min_idx, U32 max_idx)
+{
+	for (U32 j = 0; j < inputs.getCount(); ++j)
+	{
+		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
+		{ //found vertex array
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			domVertices* vertices = (domVertices*) elem.cast();
+
+			domInputLocal_Array& v_inp = vertices->getInput_array();
+			if (inputs[j]->getOffset() != 0)
+			{
+				llerrs << "WTF?" << llendl;
+			}
+
+			for (U32 k = 0; k < v_inp.getCount(); ++k)
+			{
+				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+				{
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					
+					daeElementRef elem = uri.getElement();
+					domSource* src = (domSource*) elem.cast();
+
+					if (src->getTechnique_common()->getAccessor()->getStride() != 3)
+					{
+						llerrs << "WTF?" << llendl;
+					}
+
+					domListOfFloats& v = src->getFloat_array()->getValue();
+
+					LLVector4a min;
+					min.set(v[min_idx], v[min_idx+1], v[min_idx+2]);
+					LLVector4a max = min;
+
+					for (U32 j = min_idx; j <= max_idx; ++j)
+					{ //copy vertex array
+						face.mPositions[j-min_idx].set(v[j*3+0], v[j*3+1], v[j*3+2]);
+						update_min_max(min, max, face.mPositions[j-min_idx]);
+					}
+
+					face.mExtents[0] = min;
+					face.mExtents[1] = max;
+				}
+			}
+		}
+
+		if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
+		{
+			//found normal array for this triangle list
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			domSource* src = (domSource*) elem.cast();
+			domListOfFloats& n = src->getFloat_array()->getValue();
+			
+			for (U32 j = min_idx; j <= max_idx; ++j)
+			{
+				LLVector4a* norm = (LLVector4a*) face.mNormals + (j-min_idx);
+				norm->set(n[j*3+0], n[j*3+1], n[j*3+2]);
+				norm->normalize3();
+			}
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
+		{ //found texCoords
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			domSource* src = (domSource*) elem.cast();
+			domListOfFloats& u = src->getFloat_array()->getValue();
+			
+			for (U32 j = min_idx; j <= max_idx; ++j)
+			{
+				face.mTexCoords[j-min_idx].setVec(u[j*2+0], u[j*2+1]);
+			}
+		}
+	}
+}
+
+void get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride,
+					 domSource* &pos_source, domSource* &tc_source, domSource* &norm_source)
+{
+	idx_stride = 0;
+
+	for (U32 j = 0; j < inputs.getCount(); ++j)
+	{
+		idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride);
+
+		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
+		{ //found vertex array
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			domVertices* vertices = (domVertices*) elem.cast();
+
+			domInputLocal_Array& v_inp = vertices->getInput_array();
+			
+			
+			for (U32 k = 0; k < v_inp.getCount(); ++k)
+			{
+				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+				{
+					pos_offset = inputs[j]->getOffset();
+
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					daeElementRef elem = uri.getElement();
+					pos_source = (domSource*) elem.cast();
+				}
+				
+				if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0)
+				{
+					norm_offset = inputs[j]->getOffset();
+
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					daeElementRef elem = uri.getElement();
+					norm_source = (domSource*) elem.cast();
+				}
+			}
+		}
+
+		if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
+		{
+			//found normal array for this triangle list
+			norm_offset = inputs[j]->getOffset();
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			norm_source = (domSource*) elem.cast();
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
+		{ //found texCoords
+			tc_offset = inputs[j]->getOffset();
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			tc_source = (domSource*) elem.cast();
+		}
+	}
+
+	idx_stride += 1;
+}
+
+LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri)
+{
+	LLVolumeFace face;
+	std::vector<LLVolumeFace::VertexData> verts;
+	std::vector<U16> indices;
+	
+	const domInputLocalOffset_Array& inputs = tri->getInput_array();
+
+	S32 pos_offset = -1;
+	S32 tc_offset = -1;
+	S32 norm_offset = -1;
+
+	domSource* pos_source = NULL;
+	domSource* tc_source = NULL;
+	domSource* norm_source = NULL;
+
+	S32 idx_stride = 0;
+
+	get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source);
+
+	domPRef p = tri->getP();
+	domListOfUInts& idx = p->getValue();
+	
+	domListOfFloats  dummy ;
+	domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy ;
+	domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy ;
+	domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy ;
+
+	if (pos_source)
+	{
+		face.mExtents[0].set(v[0], v[1], v[2]);
+		face.mExtents[1].set(v[0], v[1], v[2]);
+	}
+	
+	LLVolumeFace::VertexMapData::PointMap point_map;
+	
+	for (U32 i = 0; i < idx.getCount(); i += idx_stride)
+	{
+		LLVolumeFace::VertexData cv;
+		if (pos_source)
+		{
+			cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0],
+								v[idx[i+pos_offset]*3+1],
+								v[idx[i+pos_offset]*3+2]));
+		}
+
+		if (tc_source)
+		{
+			cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0],
+								tc[idx[i+tc_offset]*2+1]);
+		}
+
+		if (norm_source)
+		{
+			cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0],
+								n[idx[i+norm_offset]*3+1],
+								n[idx[i+norm_offset]*3+2]));
+		}
+
+		
+		BOOL found = FALSE;
+			
+		LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
+		point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
+		
+		if (point_iter != point_map.end())
+		{
+			for (U32 j = 0; j < point_iter->second.size(); ++j)
+			{
+				if ((point_iter->second)[j] == cv)
+				{
+					found = TRUE;
+					indices.push_back((point_iter->second)[j].mIndex);
+					break;
+				}
+			}
+		}
+
+		if (!found)
+		{
+			update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
+			verts.push_back(cv);
+			if (verts.size() >= 65535)
+			{
+				//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl;
+				return LLModel::VERTEX_NUMBER_OVERFLOW ;
+			}
+			U16 index = (U16) (verts.size()-1);
+			indices.push_back(index);
+
+			LLVolumeFace::VertexMapData d;
+			d.setPosition(cv.getPosition());
+			d.mTexCoord = cv.mTexCoord;
+			d.setNormal(cv.getNormal());
+			d.mIndex = index;
+			if (point_iter != point_map.end())
+			{
+				point_iter->second.push_back(d);
+			}
+			else
+			{
+				point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
+			}
+		}
+
+		if (indices.size()%3 == 0 && verts.size() >= 65532)
+		{
+			face_list.push_back(face);
+			face_list.rbegin()->fillFromLegacyData(verts, indices);
+			face = LLVolumeFace();
+			point_map.clear();
+		}
+
+	}
+
+	if (!verts.empty())
+	{
+		std::string material;
+
+		if (tri->getMaterial())
+		{
+			material = std::string(tri->getMaterial());
+		}
+		
+		materials.push_back(material);
+		face_list.push_back(face);
+
+		face_list.rbegin()->fillFromLegacyData(verts, indices);
+	}
+
+	return LLModel::NO_ERRORS ;
+}
+
+LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly)
+{
+	domPRef p = poly->getP();
+	domListOfUInts& idx = p->getValue();
+
+	if (idx.getCount() == 0)
+	{
+		return LLModel::NO_ERRORS ;
+	}
+
+	const domInputLocalOffset_Array& inputs = poly->getInput_array();
+
+
+	domListOfUInts& vcount = poly->getVcount()->getValue();
+	
+	S32 pos_offset = -1;
+	S32 tc_offset = -1;
+	S32 norm_offset = -1;
+
+	domSource* pos_source = NULL;
+	domSource* tc_source = NULL;
+	domSource* norm_source = NULL;
+
+	S32 idx_stride = 0;
+
+	get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source);
+
+	LLVolumeFace face;
+
+	std::vector<U16> indices;
+	std::vector<LLVolumeFace::VertexData> verts;
+
+	domListOfFloats v;
+	domListOfFloats tc;
+	domListOfFloats n;
+
+	if (pos_source)
+	{
+		v = pos_source->getFloat_array()->getValue();
+		face.mExtents[0].set(v[0], v[1], v[2]);
+		face.mExtents[1].set(v[0], v[1], v[2]);
+	}
+
+	if (tc_source)
+	{
+		tc = tc_source->getFloat_array()->getValue();
+	}
+
+	if (norm_source)
+	{
+		n = norm_source->getFloat_array()->getValue();
+	}
+	
+	LLVolumeFace::VertexMapData::PointMap point_map;
+
+	U32 cur_idx = 0;
+	for (U32 i = 0; i < vcount.getCount(); ++i)
+	{ //for each polygon
+		U32 first_index = 0;
+		U32 last_index = 0;
+		for (U32 j = 0; j < vcount[i]; ++j)
+		{ //for each vertex
+
+			LLVolumeFace::VertexData cv;
+
+			if (pos_source)
+			{
+				cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0],
+									v[idx[cur_idx+pos_offset]*3+1],
+									v[idx[cur_idx+pos_offset]*3+2]);
+			}
+
+			if (tc_source)
+			{
+				cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
+									tc[idx[cur_idx+tc_offset]*2+1]);
+			}
+
+			if (norm_source)
+			{
+				cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0],
+									n[idx[cur_idx+norm_offset]*3+1],
+									n[idx[cur_idx+norm_offset]*3+2]);
+			}
+
+			cur_idx += idx_stride;
+			
+			BOOL found = FALSE;
+				
+			LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
+			LLVector3 pos3(cv.getPosition().getF32ptr());
+			point_iter = point_map.find(pos3);
+			
+			if (point_iter != point_map.end())
+			{
+				for (U32 k = 0; k < point_iter->second.size(); ++k)
+				{
+					if ((point_iter->second)[k] == cv)
+					{
+						found = TRUE;
+						U32 index = (point_iter->second)[k].mIndex;
+						if (j == 0)
+						{
+							first_index = index;
+						}
+						else if (j == 1)
+						{
+							last_index = index;
+						}
+						else
+						{
+							indices.push_back(first_index);
+							indices.push_back(last_index);
+							indices.push_back(index);
+							last_index = index;
+						}
+
+						break;
+					}
+				}
+			}
+
+			if (!found)
+			{
+				update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
+				verts.push_back(cv);
+				if (verts.size() >= 65535)
+				{
+					//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl;
+					return LLModel::VERTEX_NUMBER_OVERFLOW ;
+				}
+				U16 index = (U16) (verts.size()-1);
+			
+				if (j == 0)
+				{
+					first_index = index;
+				}
+				else if (j == 1)
+				{
+					last_index = index;
+				}
+				else
+				{
+					indices.push_back(first_index);
+					indices.push_back(last_index);
+					indices.push_back(index);
+					last_index = index;
+				}	
+
+				LLVolumeFace::VertexMapData d;
+				d.setPosition(cv.getPosition());
+				d.mTexCoord = cv.mTexCoord;
+				d.setNormal(cv.getNormal());
+				d.mIndex = index;
+				if (point_iter != point_map.end())
+				{
+					point_iter->second.push_back(d);
+				}
+				else
+				{
+					point_map[pos3].push_back(d);
+				}
+			}
+
+			if (indices.size()%3 == 0 && indices.size() >= 65532)
+			{
+				face_list.push_back(face);
+				face_list.rbegin()->fillFromLegacyData(verts, indices);
+				face = LLVolumeFace();
+				verts.clear();
+				indices.clear();
+				point_map.clear();
+			}
+		}
+	}
+
+	if (!verts.empty())
+	{
+		std::string material;
+
+		if (poly->getMaterial())
+		{
+			material = std::string(poly->getMaterial());
+		}
+		
+		materials.push_back(material);
+		face_list.push_back(face);
+		face_list.rbegin()->fillFromLegacyData(verts, indices);
+	}
+
+	return LLModel::NO_ERRORS ;
+}
+
+LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly)
+{
+	LLVolumeFace face;
+	std::vector<U16> indices;
+	std::vector<LLVolumeFace::VertexData> verts;
+
+	const domInputLocalOffset_Array& inputs = poly->getInput_array();
+
+
+	S32 v_offset = -1;
+	S32 n_offset = -1;
+	S32 t_offset = -1;
+
+	domListOfFloats* v = NULL;
+	domListOfFloats* n = NULL;
+	domListOfFloats* t = NULL;
+	
+	U32 stride = 0;
+	for (U32 i = 0; i < inputs.getCount(); ++i)
+	{
+		stride = llmax((U32) inputs[i]->getOffset()+1, stride);
+
+		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0)
+		{ //found vertex array
+			v_offset = inputs[i]->getOffset();
+
+			const domURIFragmentType& uri = inputs[i]->getSource();
+			daeElementRef elem = uri.getElement();
+			domVertices* vertices = (domVertices*) elem.cast();
+
+			domInputLocal_Array& v_inp = vertices->getInput_array();
+
+			for (U32 k = 0; k < v_inp.getCount(); ++k)
+			{
+				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+				{
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					daeElementRef elem = uri.getElement();
+					domSource* src = (domSource*) elem.cast();
+					v = &(src->getFloat_array()->getValue());
+				}
+			}
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0)
+		{
+			n_offset = inputs[i]->getOffset();
+			//found normal array for this triangle list
+			const domURIFragmentType& uri = inputs[i]->getSource();
+			daeElementRef elem = uri.getElement();
+			domSource* src = (domSource*) elem.cast();
+			n = &(src->getFloat_array()->getValue());
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0)
+		{ //found texCoords
+			t_offset = inputs[i]->getOffset();
+			const domURIFragmentType& uri = inputs[i]->getSource();
+			daeElementRef elem = uri.getElement();
+			domSource* src = (domSource*) elem.cast();
+			t = &(src->getFloat_array()->getValue());
+		}
+	}
+
+	domP_Array& ps = poly->getP_array();
+
+	//make a triangle list in <verts>
+	for (U32 i = 0; i < ps.getCount(); ++i)
+	{ //for each polygon
+		domListOfUInts& idx = ps[i]->getValue();
+		for (U32 j = 0; j < idx.getCount()/stride; ++j)
+		{ //for each vertex
+			if (j > 2)
+			{
+				U32 size = verts.size();
+				LLVolumeFace::VertexData v0 = verts[size-3];
+				LLVolumeFace::VertexData v1 = verts[size-1];
+
+				verts.push_back(v0);
+				verts.push_back(v1);
+			}
+
+			LLVolumeFace::VertexData vert;
+
+
+			if (v)
+			{
+				U32 v_idx = idx[j*stride+v_offset]*3;
+				vert.getPosition().set(v->get(v_idx),
+								v->get(v_idx+1),
+								v->get(v_idx+2));
+			}
+			
+			if (n)
+			{
+				U32 n_idx = idx[j*stride+n_offset]*3;
+				vert.getNormal().set(n->get(n_idx),
+								n->get(n_idx+1),
+								n->get(n_idx+2));
+			}
+
+			if (t)
+			{
+				U32 t_idx = idx[j*stride+t_offset]*2;
+				vert.mTexCoord.setVec(t->get(t_idx),
+								t->get(t_idx+1));								
+			}
+		
+			
+			verts.push_back(vert);
+		}
+	}
+
+	if (verts.empty())
+	{
+		return LLModel::NO_ERRORS;
+	}
+
+	face.mExtents[0] = verts[0].getPosition();
+	face.mExtents[1] = verts[0].getPosition();
+	
+	//create a map of unique vertices to indices
+	std::map<LLVolumeFace::VertexData, U32> vert_idx;
+
+	U32 cur_idx = 0;
+	for (U32 i = 0; i < verts.size(); ++i)
+	{
+		std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]);
+		if (iter == vert_idx.end())
+		{
+			vert_idx[verts[i]] = cur_idx++;
+		}
+	}
+
+	if (cur_idx != vert_idx.size())
+	{
+		llerrs << "WTF?" << llendl;
+	}
+
+	//build vertex array from map
+	std::vector<LLVolumeFace::VertexData> new_verts;
+	new_verts.resize(vert_idx.size());
+
+	for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)
+	{
+		new_verts[iter->second] = iter->first;
+		update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());
+	}
+
+	//build index array from map
+	indices.resize(verts.size());
+
+	for (U32 i = 0; i < verts.size(); ++i)
+	{
+		indices[i] = vert_idx[verts[i]];
+	}
+
+	// DEBUG just build an expanded triangle list
+	/*for (U32 i = 0; i < verts.size(); ++i)
+	{
+		indices.push_back((U16) i);
+		update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition());
+	}*/
+
+    if (!new_verts.empty())
+	{
+		std::string material;
+
+		if (poly->getMaterial())
+		{
+			material = std::string(poly->getMaterial());
+		}
+
+		materials.push_back(material);
+		face_list.push_back(face);
+		face_list.rbegin()->fillFromLegacyData(new_verts, indices);
+	}
+
+	return LLModel::NO_ERRORS ;
+}
+
+//static
+std::string LLModel::getStatusString(U32 status)
+{
+	const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow"};
+
+	if(status < INVALID_STATUS)
+	{
+		if(status_strings[status] == std::string())
+		{
+			llerrs << "No valid status string for this status: " << (U32)status << llendl ;
+		}
+		return status_strings[status] ;
+	}
+
+	llerrs << "Invalid model status: " << (U32)status << llendl ;
+
+	return std::string() ;
+}
+
+void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh)
+{
+	domTriangles_Array& tris = mesh->getTriangles_array();
+		
+	for (U32 i = 0; i < tris.getCount(); ++i)
+	{
+		domTrianglesRef& tri = tris.get(i);
+
+		mStatus = load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri);
+		
+		if(mStatus != NO_ERRORS)
+		{
+			mVolumeFaces.clear() ;
+			mMaterialList.clear() ;
+			return ; //abort
+		}
+	}
+
+	domPolylist_Array& polys = mesh->getPolylist_array();
+	for (U32 i = 0; i < polys.getCount(); ++i)
+	{
+		domPolylistRef& poly = polys.get(i);
+
+		mStatus = load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly);
+
+		if(mStatus != NO_ERRORS)
+		{
+			mVolumeFaces.clear() ;
+			mMaterialList.clear() ;
+			return ; //abort
+		}
+	}
+
+	domPolygons_Array& polygons = mesh->getPolygons_array();
+	for (U32 i = 0; i < polygons.getCount(); ++i)
+	{
+		domPolygonsRef& poly = polygons.get(i);
+
+		mStatus = load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly);
+
+		if(mStatus != NO_ERRORS)
+		{
+			mVolumeFaces.clear() ;
+			mMaterialList.clear() ;
+			return ; //abort
+		}
+	}
+
+}
+
+BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh)
+{
+	if (mesh)
+	{
+		mVolumeFaces.clear();
+		mMaterialList.clear();
+
+		addVolumeFacesFromDomMesh(mesh);
+		
+		if (getNumVolumeFaces() > 0)
+		{
+			optimizeVolumeFaces();
+			normalizeVolumeFaces();
+
+			if (getNumVolumeFaces() > 0)
+			{
+				return TRUE;
+			}
+		}
+	}
+	else
+	{	
+		llwarns << "no mesh found" << llendl;
+	}
+	
+	return FALSE;
+}
+
+void LLModel::offsetMesh( const LLVector3& pivotPoint )
+{
+	LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] );
+	
+	for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); )
+	{
+		std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++;
+		LLVolumeFace& face = *currentFaceIt;
+		LLVector4a *pos = (LLVector4a*) face.mPositions;
+		
+		for (U32 i=0; i<face.mNumVertices; ++i )
+		{
+			pos[i].add( pivot );
+		}
+	}
+}
+
+void LLModel::optimizeVolumeFaces()
+{
+#if 0 //VECTORIZE ?
+	for (std::vector<LLVolumeFace>::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); )
+	{
+		std::vector<LLVolumeFace>::iterator cur_iter = iter++;
+		LLVolumeFace& face = *cur_iter;
+
+		for (S32 i = 0; i < (S32) face.mNumIndices; i += 3)
+		{ //remove zero area triangles
+			U16 i0 = face.mIndices[i+0];
+			U16 i1 = face.mIndices[i+1];
+			U16 i2 = face.mIndices[i+2];
+
+			if (i0 == i1 || 
+				i1 == i2 || 
+				i0 == i2)
+			{ //duplicate index in triangle, remove triangle
+				face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3);
+				i -= 3;
+			}
+			else
+			{ 
+				LLVolumeFace::VertexData& v0 = face.mVertices[i0];
+				LLVolumeFace::VertexData& v1 = face.mVertices[i1];
+				LLVolumeFace::VertexData& v2 = face.mVertices[i2];
+
+				if (v0.mPosition == v1.mPosition ||
+					v1.mPosition == v2.mPosition ||
+					v2.mPosition == v0.mPosition)
+				{ //zero area triangle, delete
+					face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3);
+					i-=3;
+				}
+			}
+		}
+
+		//remove unreference vertices
+		std::vector<bool> ref;
+		ref.resize(face.mNumVertices);
+
+		for (U32 i = 0; i < ref.size(); ++i)
+		{
+			ref[i] = false;
+		}
+
+		for (U32 i = 0; i < face.mNumIndices; ++i)
+		{ 
+			ref[face.mIndices[i]] = true;
+		}
+
+		U32 unref_count = 0;
+		for (U32 i = 0; i < ref.size(); ++i)
+		{
+			if (!ref[i])
+			{
+				//vertex is unreferenced
+				face.mVertices.erase(face.mVertices.begin()+(i-unref_count));
+				U16 idx = (U16) (i-unref_count);
+
+				for (U32 j = 0; j < face.mNumIndices; ++j)
+				{ //decrement every index array value greater than idx
+					if (face.mIndices[j] > idx)
+					{
+						--face.mIndices[j];
+					}
+				}
+				++unref_count;
+			}
+		}
+
+		if (face.mVertices.empty() || face.mIndices.empty())
+		{ //face is empty, remove it
+			iter = mVolumeFaces.erase(cur_iter);
+		}
+	}
+#endif
+}
+
+// Shrink the model to fit
+// on a 1x1x1 cube centered at the origin.
+// The positions and extents
+// multiplied by  mNormalizedScale
+// and offset by mNormalizedTranslation
+// to be the "original" extents and position.
+// Also, the positions will fit
+// within the unit cube.
+void LLModel::normalizeVolumeFaces()
+{
+
+	// ensure we don't have too many faces
+	if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES)
+		mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES);
+	
+	if (!mVolumeFaces.empty())
+	{
+		LLVector4a min, max;
+		
+		if (mVolumeFaces[0].mNumVertices <= 0)
+		{
+			llerrs << "WTF?" << llendl;
+		}
+
+		// For all of the volume faces
+		// in the model, loop over
+		// them and see what the extents
+		// of the volume along each axis.
+		min = mVolumeFaces[0].mExtents[0];
+		max = mVolumeFaces[0].mExtents[1];
+
+		for (U32 i = 1; i < mVolumeFaces.size(); ++i)
+		{
+			LLVolumeFace& face = mVolumeFaces[i];
+
+			if (face.mNumVertices <= 0)
+			{
+				llerrs << "WTF?" << llendl;
+			}
+
+			update_min_max(min, max, face.mExtents[0]);
+			update_min_max(min, max, face.mExtents[1]);
+		}
+
+		// Now that we have the extents of the model
+		// we can compute the offset needed to center
+		// the model at the origin.
+
+		// Compute center of the model
+		// and make it negative to get translation
+		// needed to center at origin.
+		LLVector4a trans;
+		trans.setAdd(min, max);
+		trans.mul(-0.5f);
+
+		// Compute the total size along all
+		// axes of the model.
+		LLVector4a size;
+		size.setSub(max, min);
+
+		// Prevent division by zero.
+		F32 x = size[0];
+		F32 y = size[1];
+		F32 z = size[2];
+		F32 w = size[3];
+		if (fabs(x)<F_APPROXIMATELY_ZERO)
+		{
+			x = 1.0;
+		}
+		if (fabs(y)<F_APPROXIMATELY_ZERO)
+		{
+			y = 1.0;
+		}
+		if (fabs(z)<F_APPROXIMATELY_ZERO)
+		{
+			z = 1.0;
+		}
+		size.set(x,y,z,w);
+
+		// Compute scale as reciprocal of size
+		LLVector4a scale;
+		scale.splat(1.f);
+		scale.div(size);
+
+		for (U32 i = 0; i < mVolumeFaces.size(); ++i)
+		{
+			LLVolumeFace& face = mVolumeFaces[i];
+
+			// We shrink the extents so
+			// that they fall within
+			// the unit cube.
+			face.mExtents[0].add(trans);
+			face.mExtents[0].mul(scale);
+
+			face.mExtents[1].add(trans);
+			face.mExtents[1].mul(scale);
+
+			// For all the positions, we scale
+			// the positions to fit within the unit cube.
+			LLVector4a* pos = (LLVector4a*) face.mPositions;
+			for (U32 j = 0; j < face.mNumVertices; ++j)
+			{
+			 	pos[j].add(trans);
+				pos[j].mul(scale);
+			}
+		}
+
+		// mNormalizedScale is the scale at which
+		// we would need to multiply the model
+		// by to get the original size of the
+		// model instead of the normalized size.
+		LLVector4a normalized_scale;
+		normalized_scale.splat(1.f);
+		normalized_scale.div(scale);
+		mNormalizedScale.set(normalized_scale.getF32ptr());
+		mNormalizedTranslation.set(trans.getF32ptr());
+		mNormalizedTranslation *= -1.f; 
+	}
+}
+
+void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out)
+{
+	scale_out = mNormalizedScale;
+	translation_out = mNormalizedTranslation;
+}
+
+void LLModel::setNumVolumeFaces(S32 count)
+{
+	mVolumeFaces.resize(count);
+}
+
+void LLModel::setVolumeFaceData(
+	S32 f, 
+	LLStrider<LLVector3> pos, 
+	LLStrider<LLVector3> norm, 
+	LLStrider<LLVector2> tc, 
+	LLStrider<U16> ind, 
+	U32 num_verts, 
+	U32 num_indices)
+{
+	LLVolumeFace& face = mVolumeFaces[f];
+
+	face.resizeVertices(num_verts);
+	face.resizeIndices(num_indices);
+
+	LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32));
+	LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32));
+	LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32));
+	U32 size = (num_indices*2+0xF)&~0xF;
+	LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size);
+}
+
+void LLModel::appendFaces(LLModel *model, LLMatrix4 &transform, LLMatrix4& norm_mat)
+{
+	if (mVolumeFaces.empty())
+	{
+		setNumVolumeFaces(1);
+	}
+
+	LLVolumeFace& face = mVolumeFaces[mVolumeFaces.size()-1];
+
+
+	for (S32 i = 0; i < model->getNumFaces(); ++i)
+	{
+		face.appendFace(model->getVolumeFace(i), transform, norm_mat);
+	}
+
+}
+
+void LLModel::appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat)
+{
+	S32 rindex = getNumVolumeFaces()-1; 
+	if (rindex == -1 || 
+		mVolumeFaces[rindex].mNumVertices + src_face.mNumVertices >= 65536)
+	{ //empty or overflow will occur, append new face
+		LLVolumeFace cur_face;
+		cur_face.appendFace(src_face, mat, norm_mat);
+		addFace(cur_face);
+		mMaterialList.push_back(src_material);
+	}
+	else
+	{ //append to existing end face
+		mVolumeFaces.rbegin()->appendFace(src_face, mat, norm_mat);
+	}
+}
+
+void LLModel::addFace(const LLVolumeFace& face)
+{
+	if (face.mNumVertices == 0)
+	{
+		llerrs << "Cannot add empty face." << llendl;
+	}
+
+	mVolumeFaces.push_back(face);
+
+	if (mVolumeFaces.size() > MAX_MODEL_FACES)
+	{
+		llerrs << "Model prims cannot have more than " << MAX_MODEL_FACES << " faces!" << llendl;
+	}
+}
+
+
+void LLModel::generateNormals(F32 angle_cutoff)
+{
+	//generate normals for all faces by:
+	// 1 - Create faceted copy of face with no texture coordinates
+	// 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals
+	// 3 - Generate smoothed set of normals based on welding results
+	// 4 - Create faceted copy of face with texture coordinates
+	// 5 - Copy smoothed normals to faceted copy, using closest normal to triangle normal where more than one normal exists for a given position
+	// 6 - Remove redundant vertices from new faceted (now smooth) copy
+
+	angle_cutoff = cosf(angle_cutoff);
+	for (U32 j = 0; j < mVolumeFaces.size(); ++j)
+	{
+		LLVolumeFace& vol_face = mVolumeFaces[j];
+
+		if (vol_face.mNumIndices > 65535)
+		{
+			llwarns << "Too many vertices for normal generation to work." << llendl;
+			continue;
+		}
+
+		//create faceted copy of current face with no texture coordinates (step 1)
+		LLVolumeFace faceted;
+
+		LLVector4a* src_pos = (LLVector4a*) vol_face.mPositions;
+		//LLVector4a* src_norm = (LLVector4a*) vol_face.mNormals;
+
+
+		faceted.resizeVertices(vol_face.mNumIndices);
+		faceted.resizeIndices(vol_face.mNumIndices);
+		//bake out triangles into temporary face, clearing texture coordinates
+		for (U32 i = 0; i < vol_face.mNumIndices; ++i)
+		{
+			U32 idx = vol_face.mIndices[i];
+		
+			faceted.mPositions[i] = src_pos[idx];
+			faceted.mTexCoords[i] = LLVector2(0,0);
+			faceted.mIndices[i] = i;
+		}
+
+		//generate normals for temporary face
+		for (U32 i = 0; i < faceted.mNumIndices; i += 3)
+		{ //for each triangle
+			U16 i0 = faceted.mIndices[i+0];
+			U16 i1 = faceted.mIndices[i+1];
+			U16 i2 = faceted.mIndices[i+2];
+			
+			LLVector4a& p0 = faceted.mPositions[i0];
+			LLVector4a& p1 = faceted.mPositions[i1];
+			LLVector4a& p2 = faceted.mPositions[i2];
+
+			LLVector4a& n0 = faceted.mNormals[i0];
+			LLVector4a& n1 = faceted.mNormals[i1];
+			LLVector4a& n2 = faceted.mNormals[i2];
+
+			LLVector4a lhs, rhs;
+			lhs.setSub(p1, p0);
+			rhs.setSub(p2, p0);
+
+			n0.setCross3(lhs, rhs);
+			n0.normalize3();
+			n1 = n0;
+			n2 = n0;
+		}
+
+		//weld vertices in temporary face, respecting angle_cutoff (step 2)
+		faceted.optimize(angle_cutoff);
+
+		//generate normals for welded face based on new topology (step 3)
+
+		for (U32 i = 0; i < faceted.mNumVertices; i++)
+		{
+			faceted.mNormals[i].clear();
+		}
+
+		for (U32 i = 0; i < faceted.mNumIndices; i += 3)
+		{ //for each triangle
+			U16 i0 = faceted.mIndices[i+0];
+			U16 i1 = faceted.mIndices[i+1];
+			U16 i2 = faceted.mIndices[i+2];
+			
+			LLVector4a& p0 = faceted.mPositions[i0];
+			LLVector4a& p1 = faceted.mPositions[i1];
+			LLVector4a& p2 = faceted.mPositions[i2];
+
+			LLVector4a& n0 = faceted.mNormals[i0];
+			LLVector4a& n1 = faceted.mNormals[i1];
+			LLVector4a& n2 = faceted.mNormals[i2];
+
+			LLVector4a lhs, rhs;
+			lhs.setSub(p1, p0);
+			rhs.setSub(p2, p0);
+
+			LLVector4a n;
+			n.setCross3(lhs, rhs);
+
+			n0.add(n);
+			n1.add(n);
+			n2.add(n);
+		}
+
+		//normalize normals and build point map
+		LLVolumeFace::VertexMapData::PointMap point_map;
+
+		for (U32 i = 0; i < faceted.mNumVertices; ++i)
+		{
+			faceted.mNormals[i].normalize3();
+
+			LLVolumeFace::VertexMapData v;
+			v.setPosition(faceted.mPositions[i]);
+			v.setNormal(faceted.mNormals[i]);
+
+			point_map[LLVector3(v.getPosition().getF32ptr())].push_back(v);
+		}
+
+		//create faceted copy of current face with texture coordinates (step 4)
+		LLVolumeFace new_face;
+
+		//bake out triangles into new face
+		new_face.resizeIndices(vol_face.mNumIndices);
+		new_face.resizeVertices(vol_face.mNumIndices);
+		
+		for (U32 i = 0; i < vol_face.mNumIndices; ++i)
+		{
+			U32 idx = vol_face.mIndices[i];
+			LLVolumeFace::VertexData v;
+			new_face.mPositions[i] = vol_face.mPositions[idx];
+			new_face.mNormals[i].clear();
+			new_face.mTexCoords[i] = vol_face.mTexCoords[idx];
+			new_face.mIndices[i] = i;
+		}
+
+		//generate normals for new face
+		for (U32 i = 0; i < new_face.mNumIndices; i += 3)
+		{ //for each triangle
+			U16 i0 = new_face.mIndices[i+0];
+			U16 i1 = new_face.mIndices[i+1];
+			U16 i2 = new_face.mIndices[i+2];
+			
+			LLVector4a& p0 = new_face.mPositions[i0];
+			LLVector4a& p1 = new_face.mPositions[i1];
+			LLVector4a& p2 = new_face.mPositions[i2];
+
+			LLVector4a& n0 = new_face.mNormals[i0];
+			LLVector4a& n1 = new_face.mNormals[i1];
+			LLVector4a& n2 = new_face.mNormals[i2];
+
+			LLVector4a lhs, rhs;
+			lhs.setSub(p1, p0);
+			rhs.setSub(p2, p0);
+
+			n0.setCross3(lhs, rhs);
+			n0.normalize3();
+			n1 = n0;
+			n2 = n0;
+		}
+
+		//swap out normals in new_face with best match from point map (step 5)
+		for (U32 i = 0; i < new_face.mNumVertices; ++i)
+		{
+			//LLVolumeFace::VertexData v = new_face.mVertices[i];
+
+			LLVector4a ref_norm = new_face.mNormals[i];
+
+			LLVolumeFace::VertexMapData::PointMap::iterator iter = point_map.find(LLVector3(new_face.mPositions[i].getF32ptr()));
+
+			if (iter != point_map.end())
+			{
+				F32 best = -2.f;
+				for (U32 k = 0; k < iter->second.size(); ++k)
+				{
+					LLVector4a& n = iter->second[k].getNormal();
+
+					if (!iter->second[k].getPosition().equals3(new_face.mPositions[i]))
+					{
+						llerrs << "WTF?" << llendl;
+					}
+
+					F32 cur = n.dot3(ref_norm).getF32();
+
+					if (cur > best)
+					{
+						best = cur;
+						new_face.mNormals[i] = n;
+					}
+				}
+			}
+		}
+		
+		//remove redundant vertices from new face (step 6)
+		new_face.optimize();
+
+		mVolumeFaces[j] = new_face;
+	}
+}
+
+//static
+std::string LLModel::getElementLabel(daeElement *element)
+{ // try to get a decent label for this element
+	// if we have a name attribute, use it
+	std::string name = element->getAttribute("name");
+	if (name.length())
+	{
+		return name;
+	}
+
+	// if we have an ID attribute, use it
+	if (element->getID())
+	{
+		return std::string(element->getID());
+	}
+
+	// if we have a parent, use it
+	daeElement* parent = element->getParent();
+	if (parent)
+	{
+		// if parent has a name, use it
+		std::string name = parent->getAttribute("name");
+		if (name.length())
+		{
+			return name;
+		}
+
+		// if parent has an ID, use it
+		if (parent->getID())
+		{
+			return std::string(parent->getID());
+		}
+	}
+
+	// try to use our type
+	daeString element_name = element->getElementName();
+	if (element_name)
+	{
+		return std::string(element_name);
+	}
+
+	// if all else fails, use "object"
+	return std::string("object");
+}
+
+//static 
+LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh)
+{
+	LLVolumeParams volume_params;
+	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+	LLModel* ret = new LLModel(volume_params, 0.f); 
+	ret->createVolumeFacesFromDomMesh(mesh);
+	ret->mLabel = getElementLabel(mesh);
+	return ret;
+}
+
+std::string LLModel::getName() const
+{
+	if (!mRequestedLabel.empty())
+		return mRequestedLabel;
+	else
+		return mLabel;
+}
+
+//static
+LLSD LLModel::writeModel(
+	std::ostream& ostr,
+	LLModel* physics,
+	LLModel* high,
+	LLModel* medium,
+	LLModel* low,
+	LLModel* impostor,
+	const LLModel::Decomposition& decomp,
+	BOOL upload_skin,
+	BOOL upload_joints,
+	BOOL nowrite)
+{
+	LLSD mdl;
+
+	LLModel* model[] = 
+	{
+		impostor,
+		low,
+		medium,
+		high,
+		physics
+	};
+
+	bool skinning = upload_skin && high && !high->mSkinWeights.empty();
+
+	if (skinning)
+	{ //write skinning block
+		mdl["skin"] = high->mSkinInfo.asLLSD(upload_joints);
+	}
+
+	if (!decomp.mBaseHull.empty() ||
+		!decomp.mHull.empty())		
+	{
+		mdl["decomposition"] = decomp.asLLSD();
+	}
+
+	for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx)
+	{
+		if (model[idx] && model[idx]->getNumVolumeFaces() > 0)
+		{
+			LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr());
+			LLVector3 max_pos = min_pos;
+
+			//find position domain
+			for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i)
+			{ //for each face
+				const LLVolumeFace& face = model[idx]->getVolumeFace(i);
+				for (U32 j = 0; j < face.mNumVertices; ++j)
+				{
+					update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr());
+				}
+			}
+
+			LLVector3 pos_range = max_pos - min_pos;
+
+			for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i)
+			{ //for each face
+				const LLVolumeFace& face = model[idx]->getVolumeFace(i);
+				if (!face.mNumVertices)
+				{ //don't export an empty face
+					continue;
+				}
+				LLSD::Binary verts(face.mNumVertices*3*2);
+				LLSD::Binary tc(face.mNumVertices*2*2);
+				LLSD::Binary normals(face.mNumVertices*3*2);
+				LLSD::Binary indices(face.mNumIndices*2);
+
+				U32 vert_idx = 0;
+				U32 norm_idx = 0;
+				U32 tc_idx = 0;
+			
+				LLVector2* ftc = (LLVector2*) face.mTexCoords;
+				LLVector2 min_tc = ftc[0];
+				LLVector2 max_tc = min_tc;
+	
+				//get texture coordinate domain
+				for (U32 j = 0; j < face.mNumVertices; ++j)
+				{
+					update_min_max(min_tc, max_tc, ftc[j]);
+				}
+
+				LLVector2 tc_range = max_tc - min_tc;
+
+				for (U32 j = 0; j < face.mNumVertices; ++j)
+				{ //for each vert
+		
+					F32* pos = face.mPositions[j].getF32ptr();
+					F32* norm = face.mNormals[j].getF32ptr();
+
+					//position + normal
+					for (U32 k = 0; k < 3; ++k)
+					{ //for each component
+
+						//convert to 16-bit normalized across domain
+						U16 val = (U16) (((pos[k]-min_pos.mV[k])/pos_range.mV[k])*65535);
+
+						U8* buff = (U8*) &val;
+						//write to binary buffer
+						verts[vert_idx++] = buff[0];
+						verts[vert_idx++] = buff[1];
+						
+						//convert to 16-bit normalized
+						val = (U16) ((norm[k]+1.f)*0.5f*65535);
+
+						//write to binary buffer
+						normals[norm_idx++] = buff[0];
+						normals[norm_idx++] = buff[1];
+					}
+
+					F32* src_tc = (F32*) face.mTexCoords[j].mV;
+
+					//texcoord
+					for (U32 k = 0; k < 2; ++k)
+					{ //for each component
+						//convert to 16-bit normalized
+						U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535);
+
+						U8* buff = (U8*) &val;
+						//write to binary buffer
+						tc[tc_idx++] = buff[0];
+						tc[tc_idx++] = buff[1];
+					}
+					
+				}
+
+				U32 idx_idx = 0;
+				for (U32 j = 0; j < face.mNumIndices; ++j)
+				{
+					U8* buff = (U8*) &(face.mIndices[j]);
+					indices[idx_idx++] = buff[0];
+					indices[idx_idx++] = buff[1];
+				}
+
+				//write out face data
+				mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue();
+				mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue();
+
+				mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();
+				mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue();
+
+				mdl[model_names[idx]][i]["Position"] = verts;
+				mdl[model_names[idx]][i]["Normal"] = normals;
+				mdl[model_names[idx]][i]["TexCoord0"] = tc;
+				mdl[model_names[idx]][i]["TriangleList"] = indices;
+
+				if (skinning)
+				{
+					//write out skin weights
+
+					//each influence list entry is up to 4 24-bit values
+					// first 8 bits is bone index
+					// last 16 bits is bone influence weight
+					// a bone index of 0xFF signifies no more influences for this vertex
+
+					std::stringstream ostr;
+
+					for (U32 j = 0; j < face.mNumVertices; ++j)
+					{
+						LLVector3 pos(face.mPositions[j].getF32ptr());
+
+						weight_list& weights = high->getJointInfluences(pos);
+
+						if (weights.size() > 4)
+						{
+							llerrs << "WTF?" << llendl;
+						}
+
+						S32 count = 0;
+						for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter)
+						{
+							if (iter->mJointIdx < 255 && iter->mJointIdx >= 0)
+							{
+								U8 idx = (U8) iter->mJointIdx;
+								ostr.write((const char*) &idx, 1);
+
+								U16 influence = (U16) (iter->mWeight*65535);
+								ostr.write((const char*) &influence, 2);
+
+								++count;
+							}		
+						}
+						U8 end_list = 0xFF;
+						if (count < 4)
+						{
+							ostr.write((const char*) &end_list, 1);
+						}
+					}
+
+					//copy ostr to binary buffer
+					std::string data = ostr.str();
+					const U8* buff = (U8*) data.data();
+					U32 bytes = data.size();
+
+					LLSD::Binary w(bytes);
+					for (U32 j = 0; j < bytes; ++j)
+					{
+						w[j] = buff[j];
+					}
+
+					mdl[model_names[idx]][i]["Weights"] = w;
+				}
+			}
+		}
+	}
+	
+	return writeModelToStream(ostr, mdl, nowrite);
+}
+
+LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite)
+{
+	U32 bytes = 0;
+	
+	std::string::size_type cur_offset = 0;
+
+	LLSD header;
+
+	std::string skin;
+
+	if (mdl.has("skin"))
+	{ //write out skin block
+		skin = zip_llsd(mdl["skin"]);
+
+		U32 size = skin.size();
+		if (size > 0)
+		{
+			header["skin"]["offset"] = (LLSD::Integer) cur_offset;
+			header["skin"]["size"] = (LLSD::Integer) size;
+			cur_offset += size;
+			bytes += size;
+		}
+		else
+		{
+			llerrs << "WTF?" << llendl;
+		}
+	}
+
+	std::string decomposition;
+
+	if (mdl.has("decomposition"))
+	{ //write out convex decomposition
+		decomposition = zip_llsd(mdl["decomposition"]);
+
+		U32 size = decomposition.size();
+		if (size > 0)
+		{
+			header["decomposition"]["offset"] = (LLSD::Integer) cur_offset;
+			header["decomposition"]["size"] = (LLSD::Integer) size;
+			cur_offset += size;
+			bytes += size;
+		}
+	}
+
+	std::string out[MODEL_NAMES_LENGTH];
+
+	for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
+	{
+		if (mdl.has(model_names[i]))
+		{
+			out[i] = zip_llsd(mdl[model_names[i]]);
+
+			U32 size = out[i].size();
+
+			header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset;
+			header[model_names[i]]["size"] = (LLSD::Integer) size;
+			cur_offset += size;
+			bytes += size;
+		}
+		else
+		{
+			header[model_names[i]]["offset"] = -1;
+			header[model_names[i]]["size"] = 0;
+		}
+	}
+
+	if (!nowrite)
+	{
+		LLSDSerialize::toBinary(header, ostr);
+
+		if (!skin.empty())
+		{ //write skin block
+			ostr.write((const char*) skin.data(), header["skin"]["size"].asInteger());
+		}
+
+		if (!decomposition.empty())
+		{ //write decomposition block
+			ostr.write((const char*) decomposition.data(), header["decomposition"]["size"].asInteger());
+		}
+
+		for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
+		{
+			if (!out[i].empty())
+			{
+				ostr.write((const char*) out[i].data(), header[model_names[i]]["size"].asInteger());
+			}
+		}
+	}
+	
+	return header;
+}
+
+LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
+{
+	weight_map::iterator iter = mSkinWeights.find(pos);
+	
+	if (iter != mSkinWeights.end())
+	{
+		if ((iter->first - pos).magVec() > 0.1f)
+		{
+			llerrs << "WTF?" << llendl;
+		}
+
+		return iter->second;
+	}
+	else
+	{  //no exact match found, get closest point
+		const F32 epsilon = 2.f/65536;
+		weight_map::iterator iter_up = mSkinWeights.lower_bound(pos);
+		weight_map::iterator iter_down = ++iter_up;
+
+		weight_map::iterator best = iter_up;
+
+		F32 min_dist = (iter->first - pos).magVecSquared();
+
+		bool done = false;
+		while (!done)
+		{ //search up and down mSkinWeights from lower bound of pos until a 
+		  //match is found within epsilon.  If no match is found within epsilon,
+		  //return closest match
+			done = true;
+			if (iter_up != mSkinWeights.end() && ++iter_up != mSkinWeights.end())
+			{
+				done = false;
+				F32 dist = (iter_up->first - pos).magVecSquared();
+
+				if (dist < epsilon)
+				{
+					return iter_up->second;
+				}
+
+				if (dist < min_dist)
+				{
+					best = iter_up;
+					min_dist = dist;
+				}
+			}
+
+			if (iter_down != mSkinWeights.begin() && --iter_down != mSkinWeights.begin())
+			{
+				done = false;
+
+				F32 dist = (iter_down->first - pos).magVecSquared();
+
+				if (dist < epsilon)
+				{
+					return iter_down->second;
+				}
+
+				if (dist < min_dist)
+				{
+					best = iter_down;
+					min_dist = dist;
+				}
+
+			}
+		}
+		
+		return best->second;
+	}					
+}
+
+void LLModel::setConvexHullDecomposition(
+	const LLModel::convex_hull_decomposition& decomp)
+{
+	mPhysics.mHull = decomp;
+	mPhysics.mMesh.clear();
+	updateHullCenters();
+}
+
+void LLModel::updateHullCenters()
+{
+	mHullCenter.resize(mPhysics.mHull.size());
+	mHullPoints = 0;
+	mCenterOfHullCenters.clear();
+
+	for (U32 i = 0; i < mPhysics.mHull.size(); ++i)
+	{
+		LLVector3 cur_center;
+
+		for (U32 j = 0; j < mPhysics.mHull[i].size(); ++j)
+		{
+			cur_center += mPhysics.mHull[i][j];
+		}
+		mCenterOfHullCenters += cur_center;
+		cur_center *= 1.f/mPhysics.mHull[i].size();
+		mHullCenter[i] = cur_center;
+		mHullPoints += mPhysics.mHull[i].size();
+	}
+
+	if (mHullPoints > 0)
+	{
+		mCenterOfHullCenters *= 1.f / mHullPoints;
+		llassert(mPhysics.asLLSD().has("HullList"));
+	}
+}
+
+bool LLModel::loadModel(std::istream& is)
+{
+	mSculptLevel = -1;  // default is an error occured
+
+	LLSD header;
+	{
+		if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024))
+		{
+			llwarns << "Mesh header parse error.  Not a valid mesh asset!" << llendl;
+			return false;
+		}
+	}
+	
+	std::string nm[] = 
+	{
+		"lowest_lod",
+		"low_lod",
+		"medium_lod",
+		"high_lod",
+		"physics_shape",
+	};
+
+	const S32 MODEL_LODS = 5;
+
+	S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS);
+
+	if (header[nm[lod]]["offset"].asInteger() == -1 || 
+		header[nm[lod]]["size"].asInteger() == 0 )
+	{ //cannot load requested LOD
+		return false;
+	}
+
+	bool has_skin = header["skin"]["offset"].asInteger() >=0 &&
+					header["skin"]["size"].asInteger() > 0;
+
+	if (lod == LLModel::LOD_HIGH)
+	{ //try to load skin info and decomp info
+		std::ios::pos_type cur_pos = is.tellg();
+		loadSkinInfo(header, is);
+		is.seekg(cur_pos);
+	}
+
+	if (lod == LLModel::LOD_PHYSICS)
+	{
+		std::ios::pos_type cur_pos = is.tellg();
+		loadDecomposition(header, is);
+		is.seekg(cur_pos);
+	}
+
+	is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur);
+
+	if (unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger()))
+	{
+		if (has_skin)
+		{ 
+			//build out mSkinWeight from face info
+			for (S32 i = 0; i < getNumVolumeFaces(); ++i)
+			{
+				const LLVolumeFace& face = getVolumeFace(i);
+
+				if (face.mWeights)
+				{
+					for (S32 j = 0; j < face.mNumVertices; ++j)
+					{
+						LLVector4a& w = face.mWeights[j];
+
+						std::vector<JointWeight> wght;
+
+						for (S32 k = 0; k < 4; ++k)
+						{
+							S32 idx = (S32) w[k];
+							F32 f = w[k] - idx;
+							if (f > 0.f)
+							{
+								wght.push_back(JointWeight(idx, f));
+							}
+						}
+
+						if (!wght.empty())
+						{
+							LLVector3 pos(face.mPositions[j].getF32ptr());
+							mSkinWeights[pos] = wght;
+						}
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	return false;
+
+}
+
+
+bool LLModel::loadSkinInfo(LLSD& header, std::istream &is)
+{
+	S32 offset = header["skin"]["offset"].asInteger();
+	S32 size = header["skin"]["size"].asInteger();
+
+	if (offset >= 0 && size > 0)
+	{
+		is.seekg(offset, std::ios_base::cur);
+
+		LLSD skin_data;
+
+		if (unzip_llsd(skin_data, is, size))
+		{
+			mSkinInfo.fromLLSD(skin_data);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool LLModel::loadDecomposition(LLSD& header, std::istream& is)
+{
+	S32 offset = header["decomposition"]["offset"].asInteger();
+	S32 size = header["decomposition"]["size"].asInteger();
+
+	if (offset >= 0 && size > 0)
+	{
+		is.seekg(offset, std::ios_base::cur);
+
+		LLSD data;
+
+		if (unzip_llsd(data, is, size))
+		{
+			mPhysics.fromLLSD(data);
+			updateHullCenters();
+		}
+	}
+
+	return true;
+}
+
+
+LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin)
+{
+	fromLLSD(skin);
+}
+
+void LLMeshSkinInfo::fromLLSD(LLSD& skin)
+{
+	if (skin.has("joint_names"))
+	{
+		for (U32 i = 0; i < skin["joint_names"].size(); ++i)
+		{
+			mJointNames.push_back(skin["joint_names"][i]);
+		}
+	}
+
+	if (skin.has("inverse_bind_matrix"))
+	{
+		for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i)
+		{
+			LLMatrix4 mat;
+			for (U32 j = 0; j < 4; j++)
+			{
+				for (U32 k = 0; k < 4; k++)
+				{
+					mat.mMatrix[j][k] = skin["inverse_bind_matrix"][i][j*4+k].asReal();
+				}
+			}
+
+			mInvBindMatrix.push_back(mat);
+		}
+	}
+
+	if (skin.has("bind_shape_matrix"))
+	{
+		for (U32 j = 0; j < 4; j++)
+		{
+			for (U32 k = 0; k < 4; k++)
+			{
+				mBindShapeMatrix.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal();
+			}
+		}
+	}
+
+	if (skin.has("alt_inverse_bind_matrix"))
+	{
+		for (U32 i = 0; i < skin["alt_inverse_bind_matrix"].size(); ++i)
+		{
+			LLMatrix4 mat;
+			for (U32 j = 0; j < 4; j++)
+			{
+				for (U32 k = 0; k < 4; k++)
+				{
+					mat.mMatrix[j][k] = skin["alt_inverse_bind_matrix"][i][j*4+k].asReal();
+				}
+			}
+			
+			mAlternateBindMatrix.push_back(mat);
+		}
+	}
+
+	if (skin.has("pelvis_offset"))
+	{
+		mPelvisOffset = skin["pelvis_offset"].asReal();
+	}
+}
+
+LLSD LLMeshSkinInfo::asLLSD(bool include_joints) const
+{
+	LLSD ret;
+
+	for (U32 i = 0; i < mJointNames.size(); ++i)
+	{
+		ret["joint_names"][i] = mJointNames[i];
+
+		for (U32 j = 0; j < 4; j++)
+		{
+			for (U32 k = 0; k < 4; k++)
+			{
+				ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k]; 
+			}
+		}
+	}
+
+	for (U32 i = 0; i < 4; i++)
+	{
+		for (U32 j = 0; j < 4; j++)
+		{
+			ret["bind_shape_matrix"][i*4+j] = mBindShapeMatrix.mMatrix[i][j];
+		}
+	}
+		
+	if ( include_joints && mAlternateBindMatrix.size() > 0 )
+	{
+		for (U32 i = 0; i < mJointNames.size(); ++i)
+		{
+			for (U32 j = 0; j < 4; j++)
+			{
+				for (U32 k = 0; k < 4; k++)
+				{
+					ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k]; 
+				}
+			}
+		}
+
+		ret["pelvis_offset"] = mPelvisOffset;
+	}
+
+	return ret;
+}
+
+LLModel::Decomposition::Decomposition(LLSD& data)
+{
+	fromLLSD(data);
+}
+
+void LLModel::Decomposition::fromLLSD(LLSD& decomp)
+{
+	if (decomp.has("HullList"))
+	{
+		// updated for const-correctness. gcc is picky about this type of thing - Nyx
+		const LLSD::Binary& hulls = decomp["HullList"].asBinary();
+		const LLSD::Binary& position = decomp["Position"].asBinary();
+
+		U16* p = (U16*) &position[0];
+
+		mHull.resize(hulls.size());
+
+		LLVector3 min;
+		LLVector3 max;
+		LLVector3 range;
+
+		min.setValue(decomp["Min"]);
+		max.setValue(decomp["Max"]);
+		range = max-min;
+
+		
+		for (U32 i = 0; i < hulls.size(); ++i)
+		{
+			U16 count = (hulls[i] == 0) ? 256 : hulls[i];
+			
+			std::set<U64> valid;
+
+			//must have at least 4 points
+			//llassert(count > 3);
+
+			for (U32 j = 0; j < count; ++j)
+			{
+				U64 test = (U64) p[0] | ((U64) p[1] << 16) | ((U64) p[2] << 32);
+				//point must be unique
+				//llassert(valid.find(test) == valid.end());
+				valid.insert(test);
+				mHull[i].push_back(LLVector3(
+					(F32) p[0]/65535.f*range.mV[0]+min.mV[0],
+					(F32) p[1]/65535.f*range.mV[1]+min.mV[1],
+					(F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
+				p += 3;
+
+
+			}
+
+			//each hull must contain at least 4 unique points
+			//llassert(valid.size() > 3);
+		}
+	}
+
+	if (decomp.has("Hull"))
+	{
+		const LLSD::Binary& position = decomp["Hull"].asBinary();
+
+		U16* p = (U16*) &position[0];
+
+		LLVector3 min;
+		LLVector3 max;
+		LLVector3 range;
+
+		if (decomp.has("Min"))
+		{
+			min.setValue(decomp["Min"]);
+			max.setValue(decomp["Max"]);
+		}
+		else
+		{
+			min.set(-0.5f, -0.5f, -0.5f);
+			max.set(0.5f, 0.5f, 0.5f);
+		}
+
+		range = max-min;
+
+		U16 count = position.size()/6;
+		
+		for (U32 j = 0; j < count; ++j)
+		{
+			mBaseHull.push_back(LLVector3(
+				(F32) p[0]/65535.f*range.mV[0]+min.mV[0],
+				(F32) p[1]/65535.f*range.mV[1]+min.mV[1],
+				(F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
+			p += 3;
+		}		 
+	}
+	else
+	{
+		//empty base hull mesh to indicate decomposition has been loaded
+		//but contains no base hull
+		mBaseHullMesh.clear();;
+	}
+}
+
+LLSD LLModel::Decomposition::asLLSD() const
+{
+	LLSD ret;
+	
+	if (mBaseHull.empty() && mHull.empty())
+	{ //nothing to write
+		return ret;
+	}
+
+	//write decomposition block
+	// ["decomposition"]["HullList"] -- list of 8 bit integers, each entry represents a hull with specified number of points
+	// ["decomposition"]["PositionDomain"]["Min"/"Max"]
+	// ["decomposition"]["Position"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points
+	// ["decomposition"]["Hull"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points representing a single hull approximation of given shape
+	
+	
+	//get minimum and maximum
+	LLVector3 min;
+	
+	if (mHull.empty())
+	{  
+		min = mBaseHull[0];
+	}
+	else
+	{
+		min = mHull[0][0];
+	}
+
+	LLVector3 max = min;
+
+	LLSD::Binary hulls(mHull.size());
+
+	U32 total = 0;
+
+	for (U32 i = 0; i < mHull.size(); ++i)
+	{
+		U32 size = mHull[i].size();
+		total += size;
+		hulls[i] = (U8) (size);
+
+		for (U32 j = 0; j < mHull[i].size(); ++j)
+		{
+			update_min_max(min, max, mHull[i][j]);
+		}
+	}
+
+	for (U32 i = 0; i < mBaseHull.size(); ++i)
+	{
+		update_min_max(min, max, mBaseHull[i]);	
+	}
+
+	ret["Min"] = min.getValue();
+	ret["Max"] = max.getValue();
+
+	if (!hulls.empty())
+	{
+		ret["HullList"] = hulls;
+	}
+
+	if (total > 0)
+	{
+		LLSD::Binary p(total*3*2);
+
+		LLVector3 range = max-min;
+
+		U32 vert_idx = 0;
+		
+		for (U32 i = 0; i < mHull.size(); ++i)
+		{
+			std::set<U64> valid;
+
+			llassert(!mHull[i].empty());
+
+			for (U32 j = 0; j < mHull[i].size(); ++j)
+			{
+				U64 test = 0;
+				for (U32 k = 0; k < 3; k++)
+				{
+					//convert to 16-bit normalized across domain
+					U16 val = (U16) (((mHull[i][j].mV[k]-min.mV[k])/range.mV[k])*65535);
+
+					switch (k)
+					{
+						case 0: test = test | (U64) val; break;
+						case 1: test = test | ((U64) val << 16); break;
+						case 2: test = test | ((U64) val << 32); break;
+					};
+
+					valid.insert(test);
+					
+					U8* buff = (U8*) &val;
+					//write to binary buffer
+					p[vert_idx++] = buff[0];
+					p[vert_idx++] = buff[1];
+
+					//makes sure we haven't run off the end of the array
+					llassert(vert_idx <= p.size());
+				}
+			}
+
+			//must have at least 4 unique points
+			llassert(valid.size() > 3);
+		}
+
+		ret["Position"] = p;
+	}
+
+	if (!mBaseHull.empty())
+	{
+		LLSD::Binary p(mBaseHull.size()*3*2);
+
+		LLVector3 range = max-min;
+
+		U32 vert_idx = 0;
+		for (U32 j = 0; j < mBaseHull.size(); ++j)
+		{
+			for (U32 k = 0; k < 3; k++)
+			{
+				//convert to 16-bit normalized across domain
+				U16 val = (U16) (((mBaseHull[j].mV[k]-min.mV[k])/range.mV[k])*65535);
+
+				U8* buff = (U8*) &val;
+				//write to binary buffer
+				p[vert_idx++] = buff[0];
+				p[vert_idx++] = buff[1];
+
+				if (vert_idx > p.size())
+				{
+					llerrs << "WTF?" << llendl;
+				}
+			}
+		}
+		
+		ret["Hull"] = p;
+	}
+
+	return ret;
+}
+
+void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs)
+{
+	if (!rhs)
+	{
+		return;
+	}
+
+	if (mMeshID != rhs->mMeshID)
+	{
+		llerrs << "Attempted to merge with decomposition of some other mesh." << llendl;
+	}
+
+	if (mBaseHull.empty())
+	{ //take base hull and decomposition from rhs
+		mHull = rhs->mHull;
+		mBaseHull = rhs->mBaseHull;
+		mMesh = rhs->mMesh;
+		mBaseHullMesh = rhs->mBaseHullMesh;
+	}
+
+	if (mPhysicsShapeMesh.empty())
+	{ //take physics shape mesh from rhs
+		mPhysicsShapeMesh = rhs->mPhysicsShapeMesh;
+	}
+
+	if (!mHull.empty())
+	{ //verify
+		llassert(asLLSD().has("HullList"));
+	}
+}
+
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..23f4b5cb42733a0565fc4d15d19f9976266d4595
--- /dev/null
+++ b/indra/llprimitive/llmodel.h
@@ -0,0 +1,255 @@
+/** 
+ * @file llmodel.h
+ * @brief Model handling class definitions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMODEL_H
+#define LL_LLMODEL_H
+
+#include "llpointer.h"
+#include "llvolume.h"
+#include "v4math.h"
+#include "m4math.h"
+
+class daeElement;
+class domMesh;
+
+#define MAX_MODEL_FACES 8
+
+
+class LLMeshSkinInfo 
+{
+public:
+	LLUUID mMeshID;
+	std::vector<std::string> mJointNames;
+	std::vector<LLMatrix4> mInvBindMatrix;
+	std::vector<LLMatrix4> mAlternateBindMatrix;
+	std::map<std::string, U32> mJointMap;
+
+	LLMeshSkinInfo() { }
+	LLMeshSkinInfo(LLSD& data);
+	void fromLLSD(LLSD& data);
+	LLSD asLLSD(bool include_joints) const;
+	LLMatrix4 mBindShapeMatrix;
+	float mPelvisOffset;
+};
+
+class LLModel : public LLVolume
+{
+public:
+
+	enum
+	{
+		LOD_IMPOSTOR = 0,
+		LOD_LOW,
+		LOD_MEDIUM,
+		LOD_HIGH,
+		LOD_PHYSICS,
+		NUM_LODS
+	};
+	
+	enum EModelStatus
+	{
+		NO_ERRORS = 0,
+		VERTEX_NUMBER_OVERFLOW, //vertex number is >= 65535.
+		INVALID_STATUS
+	} ;
+
+	//convex_hull_decomposition is a vector of convex hulls
+	//each convex hull is a set of points
+	typedef std::vector<std::vector<LLVector3> > convex_hull_decomposition;
+	typedef std::vector<LLVector3> hull;
+	
+	class PhysicsMesh
+	{
+	public:
+		std::vector<LLVector3> mPositions;
+		std::vector<LLVector3> mNormals;
+
+		void clear()
+		{
+			mPositions.clear();
+			mNormals.clear();
+		}
+
+		bool empty() const
+		{
+			return mPositions.empty();
+		}
+	};
+
+	class Decomposition
+	{
+	public:
+		Decomposition() { }
+		Decomposition(LLSD& data);
+		void fromLLSD(LLSD& data);
+		LLSD asLLSD() const;
+
+		void merge(const Decomposition* rhs);
+
+		LLUUID mMeshID;
+		LLModel::convex_hull_decomposition mHull;
+		LLModel::hull mBaseHull;
+
+		std::vector<LLModel::PhysicsMesh> mMesh;
+		LLModel::PhysicsMesh mBaseHullMesh;
+		LLModel::PhysicsMesh mPhysicsShapeMesh;
+	};
+
+	LLModel(LLVolumeParams& params, F32 detail);
+	~LLModel();
+
+	bool loadModel(std::istream& is);
+	bool loadSkinInfo(LLSD& header, std::istream& is);
+	bool loadDecomposition(LLSD& header, std::istream& is);
+	
+	static LLSD writeModel(
+		std::ostream& ostr,
+		LLModel* physics,
+		LLModel* high,
+		LLModel* medium,
+		LLModel* low,
+		LLModel* imposotr,
+		const LLModel::Decomposition& decomp,
+		BOOL upload_skin,
+		BOOL upload_joints,
+		BOOL nowrite = FALSE);
+
+	static LLSD writeModelToStream(
+		std::ostream& ostr,
+		LLSD& mdl,
+		BOOL nowrite = FALSE);
+
+	static LLModel* loadModelFromDomMesh(domMesh* mesh);
+	static std::string getElementLabel(daeElement* element);
+	std::string getName() const;
+	EModelStatus getStatus() const {return mStatus;}
+	static std::string getStatusString(U32 status) ;
+
+	void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform);
+	void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat);
+
+	void setNumVolumeFaces(S32 count);
+	void setVolumeFaceData(
+		S32 f, 
+		LLStrider<LLVector3> pos, 
+		LLStrider<LLVector3> norm, 
+		LLStrider<LLVector2> tc, 
+		LLStrider<U16> ind, 
+		U32 num_verts, 
+		U32 num_indices);
+
+	void generateNormals(F32 angle_cutoff);
+
+	void addFace(const LLVolumeFace& face);
+
+	void normalizeVolumeFaces();
+	void optimizeVolumeFaces();
+	void offsetMesh( const LLVector3& pivotPoint );
+	void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out);
+	std::vector<std::string> mMaterialList;
+
+	//data used for skin weights
+	class JointWeight
+	{
+	public:
+		S32 mJointIdx;
+		F32 mWeight;
+		
+		JointWeight()
+		{
+			mJointIdx = 0;
+			mWeight = 0.f;
+		}
+
+		JointWeight(S32 idx, F32 weight)
+			: mJointIdx(idx), mWeight(weight)
+		{
+		}
+
+		bool operator<(const JointWeight& rhs) const
+		{
+			if (mWeight == rhs.mWeight)
+			{
+				return mJointIdx < rhs.mJointIdx;
+			}
+
+			return mWeight < rhs.mWeight;
+		}
+
+	};
+
+	struct CompareWeightGreater
+	{
+		bool operator()(const JointWeight& lhs, const JointWeight& rhs)
+		{
+			return rhs < lhs; // strongest = first
+		}
+	};
+
+	//copy of position array for this model -- mPosition[idx].mV[X,Y,Z]
+	std::vector<LLVector3> mPosition;
+
+	//map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == <joint_index>.<weight>
+	//joint_index corresponds to mJointList
+	typedef std::vector<JointWeight> weight_list;
+	typedef std::map<LLVector3, weight_list > weight_map;
+	weight_map mSkinWeights;
+
+	//get list of weight influences closest to given position
+	weight_list& getJointInfluences(const LLVector3& pos);
+
+	LLMeshSkinInfo mSkinInfo;
+	
+	std::string mRequestedLabel; // name requested in UI, if any.
+	std::string mLabel; // name computed from dae.
+
+	LLVector3 mNormalizedScale;
+	LLVector3 mNormalizedTranslation;
+
+	float	mPelvisOffset;
+	// convex hull decomposition
+	S32 mDecompID;
+	
+	void setConvexHullDecomposition(
+		const convex_hull_decomposition& decomp);
+	void updateHullCenters();
+
+	LLVector3 mCenterOfHullCenters;
+	std::vector<LLVector3> mHullCenter;
+	U32 mHullPoints;
+
+	//ID for storing this model in a .slm file
+	S32 mLocalID;
+
+	Decomposition mPhysics;
+
+	EModelStatus mStatus ;
+protected:
+	void addVolumeFacesFromDomMesh(domMesh* mesh);
+	virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh);
+};
+
+#endif //LL_LLMODEL_H
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index f9ef897aa33626b5891eeaee3307c937a0541d8b..30532247ac1f0b8af71c4aeddbea5076b013ccfc 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -738,7 +738,11 @@ BOOL LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai
 		setNumTEs(mVolumep->getNumFaces());
 		return TRUE;
 	}
-
+	
+#if 0 
+	// #if 0'd out by davep
+	// this is a lot of cruft to set texture entry values that just stay the same for LOD switch 
+	// or immediately get overridden by an object update message, also crashes occasionally
 	U32 old_face_mask = mVolumep->mFaceMask;
 
 	S32 face_bit = 0;
@@ -936,6 +940,13 @@ BOOL LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai
 			setTE(te_num, *(old_tes.getTexture(face_mapping[face_bit])));
 		}
 	}
+#else
+	// build the new object
+	sVolumeManager->unrefVolume(mVolumep);
+	mVolumep = volumep; 
+
+	setNumTEs(mVolumep->getNumFaces());
+#endif
 	return TRUE;
 }
 
@@ -1078,7 +1089,7 @@ BOOL LLPrimitive::packTEMessage(LLMessageSystem *mesgsys) const
 	U8 packed_buffer[MAX_TE_BUFFER];
 	U8 *cur_ptr = packed_buffer;
 	
-	S32 last_face_index = getNumTEs() - 1;
+	S32 last_face_index = llmin((U32) getNumTEs(), MAX_TES) - 1;
 	
 	if (last_face_index > -1)
 	{
@@ -1359,7 +1370,7 @@ S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp)
 		return retval;
 	}
 
-	face_count = getNumTEs();
+	face_count = llmin((U32) getNumTEs(), MAX_TES);
 	U32 i;
 
 	cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)image_data, 16, face_count, MVT_LLUUID);
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index d981b248fae55d4e7c1d192fa9032bab0436b729..76faa1b8c54d5364fe964b15dc68ee2aa53388bd 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -323,7 +323,7 @@ class LLPrimitive : public LLXform
 	const LLVolume *getVolumeConst() const { return mVolumep; }		// HACK for Windoze confusion about ostream operator in LLVolume
 	LLVolume *getVolume() const { return mVolumep; }
 	virtual BOOL setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false);
-
+	
 	// Modify texture entry properties
 	inline BOOL validTE(const U8 te_num) const;
 	LLTextureEntry* getTE(const U8 te_num) const;
@@ -444,6 +444,7 @@ class LLPrimitive : public LLXform
 	U8					mNumTEs;			// # of faces on the primitve	
 	U32 				mMiscFlags;			// home for misc bools
 
+public:
 	static LLVolumeMgr* sVolumeManager;
 };
 
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index 13008292f6ae672907dc60d2097d705390e644a4..d6a31dc86236e47c3850e0b0e22eb459a8ac8027 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -271,7 +271,6 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 		}
 	}
 
-
 	const LLFontGlyphInfo* next_glyph = NULL;
 
 	const S32 GLYPH_BATCH_SIZE = 30;
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index b1a4051e96cdb8d00c909facb8738d4bbed6f227..f29ee0e57ec1e847c19929449241fa7b94853b40 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -156,30 +156,27 @@ PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB = NULL;
 PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB = NULL;
 PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB = NULL;
 
-// GL_EXT_framebuffer_object
-PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT = NULL;
-PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT = NULL;
-PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT = NULL;
-PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT = NULL;
-PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT = NULL;
-PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT = NULL;
-PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT = NULL;
-PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = NULL;
-PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = NULL;
-PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = NULL;
-PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = NULL;
-PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT = NULL;
-PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = NULL;
-PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT = NULL;
-PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = NULL;
-PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT = NULL;
-PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = NULL;
-
-// GL_EXT_framebuffer_multisample
-PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = NULL;
-
-// GL_EXT_framebuffer_blit
-PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT = NULL;
+// GL_ARB_framebuffer_object
+PFNGLISRENDERBUFFERPROC glIsRenderbuffer = NULL;
+PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = NULL;
+PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = NULL;
+PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = NULL;
+PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = NULL;
+PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv = NULL;
+PFNGLISFRAMEBUFFERPROC glIsFramebuffer = NULL;
+PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
+PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = NULL;
+PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
+PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = NULL;
+PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = NULL;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = NULL;
+PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = NULL;
+PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = NULL;
+PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv = NULL;
+PFNGLGENERATEMIPMAPPROC glGenerateMipmap = NULL;
+PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
+PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = NULL;
+PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = NULL;
 
 // GL_EXT_blend_func_separate
 PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT = NULL;
@@ -318,11 +315,12 @@ LLGLManager::LLGLManager() :
 	mIsDisabled(FALSE),
 
 	mHasMultitexture(FALSE),
+	mHasATIMemInfo(FALSE),
+	mHasNVXMemInfo(FALSE),
 	mNumTextureUnits(1),
 	mHasMipMapGeneration(FALSE),
 	mHasCompressedTextures(FALSE),
 	mHasFramebufferObject(FALSE),
-	mHasFramebufferMultisample(FALSE),
 	mHasBlendFuncSeparate(FALSE),
 
 	mHasVertexBufferObject(FALSE),
@@ -331,6 +329,7 @@ LLGLManager::LLGLManager() :
 	mHasVertexShader(FALSE),
 	mHasFragmentShader(FALSE),
 	mHasOcclusionQuery(FALSE),
+	mHasOcclusionQuery2(FALSE),
 	mHasPointParameters(FALSE),
 	mHasDrawBuffers(FALSE),
 	mHasTextureRectangle(FALSE),
@@ -502,6 +501,20 @@ bool LLGLManager::initGL()
 	// This is called here because it depends on the setting of mIsGF2or4MX, and sets up mHasMultitexture.
 	initExtensions();
 
+	if (mHasATIMemInfo)
+	{ //ask the gl how much vram is free at startup and attempt to use no more than half of that
+		S32 meminfo[4];
+		glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
+
+		mVRAM = meminfo[0]/1024;
+	}
+	else if (mHasNVXMemInfo)
+	{
+		S32 dedicated_memory;
+		glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &dedicated_memory);
+		mVRAM = dedicated_memory/1024;
+	}
+
 	if (mHasMultitexture)
 	{
 		GLint num_tex_units;		
@@ -668,11 +681,6 @@ void LLGLManager::initExtensions()
 # else
 	mHasFramebufferObject = FALSE;
 # endif // GL_EXT_framebuffer_object
-# ifdef GL_EXT_framebuffer_multisample
-	mHasFramebufferMultisample = TRUE;
-# else
-	mHasFramebufferMultisample = FALSE;
-# endif // GL_EXT_framebuffer_multisample
 # ifdef GL_ARB_draw_buffers
 	mHasDrawBuffers = TRUE;
 #else
@@ -700,6 +708,8 @@ void LLGLManager::initExtensions()
 	mHasTextureRectangle = FALSE;
 #else // LL_MESA_HEADLESS
 	mHasMultitexture = glh_init_extensions("GL_ARB_multitexture");
+	mHasATIMemInfo = ExtensionExists("GL_ATI_meminfo", gGLHExts.mSysExts);
+	mHasNVXMemInfo = ExtensionExists("GL_NVX_gpu_memory_info", gGLHExts.mSysExts);
 	mHasMipMapGeneration = glh_init_extensions("GL_SGIS_generate_mipmap");
 	mHasSeparateSpecularColor = glh_init_extensions("GL_EXT_separate_specular_color");
 	mHasAnisotropic = glh_init_extensions("GL_EXT_texture_filter_anisotropic");
@@ -708,12 +718,19 @@ void LLGLManager::initExtensions()
 	mHasARBEnvCombine = ExtensionExists("GL_ARB_texture_env_combine", gGLHExts.mSysExts);
 	mHasCompressedTextures = glh_init_extensions("GL_ARB_texture_compression");
 	mHasOcclusionQuery = ExtensionExists("GL_ARB_occlusion_query", gGLHExts.mSysExts);
+	mHasOcclusionQuery2 = ExtensionExists("GL_ARB_occlusion_query2", gGLHExts.mSysExts);
 	mHasVertexBufferObject = ExtensionExists("GL_ARB_vertex_buffer_object", gGLHExts.mSysExts);
 	mHasDepthClamp = ExtensionExists("GL_ARB_depth_clamp", gGLHExts.mSysExts) || ExtensionExists("GL_NV_depth_clamp", gGLHExts.mSysExts);
 	// mask out FBO support when packed_depth_stencil isn't there 'cause we need it for LLRenderTarget -Brad
-	mHasFramebufferObject = ExtensionExists("GL_EXT_framebuffer_object", gGLHExts.mSysExts)
-		&& ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts);
-	mHasFramebufferMultisample = mHasFramebufferObject && ExtensionExists("GL_EXT_framebuffer_multisample", gGLHExts.mSysExts);
+#ifdef GL_ARB_framebuffer_object
+	mHasFramebufferObject = ExtensionExists("GL_ARB_framebuffer_object", gGLHExts.mSysExts);
+#else
+	mHasFramebufferObject = ExtensionExists("GL_EXT_framebuffer_object", gGLHExts.mSysExts) &&
+							ExtensionExists("GL_EXT_framebuffer_blit", gGLHExts.mSysExts) &&
+							ExtensionExists("GL_EXT_framebuffer_multisample", gGLHExts.mSysExts) &&
+							ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts);
+#endif
+	
 	mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts);
 	mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts);
 	mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts);
@@ -738,7 +755,6 @@ void LLGLManager::initExtensions()
 		mHasCompressedTextures = FALSE;
 		mHasVertexBufferObject = FALSE;
 		mHasFramebufferObject = FALSE;
-		mHasFramebufferMultisample = FALSE;
 		mHasDrawBuffers = FALSE;
 		mHasBlendFuncSeparate = FALSE;
 		mHasMipMapGeneration = FALSE;
@@ -792,10 +808,9 @@ void LLGLManager::initExtensions()
 		if (strchr(blacklist,'p')) mHasPointParameters = FALSE;//S
 		if (strchr(blacklist,'q')) mHasFramebufferObject = FALSE;//S
 		if (strchr(blacklist,'r')) mHasDrawBuffers = FALSE;//S
-		if (strchr(blacklist,'s')) mHasFramebufferMultisample = FALSE;
-		if (strchr(blacklist,'t')) mHasTextureRectangle = FALSE;
-		if (strchr(blacklist,'u')) mHasBlendFuncSeparate = FALSE;//S
-		if (strchr(blacklist,'v')) mHasDepthClamp = FALSE;
+		if (strchr(blacklist,'s')) mHasTextureRectangle = FALSE;
+		if (strchr(blacklist,'t')) mHasBlendFuncSeparate = FALSE;//S
+		if (strchr(blacklist,'u')) mHasDepthClamp = FALSE;
 		
 	}
 #endif // LL_LINUX || LL_SOLARIS
@@ -828,6 +843,10 @@ void LLGLManager::initExtensions()
 	{
 		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_occlusion_query" << LL_ENDL;
 	}
+	if (!mHasOcclusionQuery2)
+	{
+		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_occlusion_query2" << LL_ENDL;
+	}
 	if (!mHasPointParameters)
 	{
 		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_point_parameters" << LL_ENDL;
@@ -895,28 +914,26 @@ void LLGLManager::initExtensions()
 	if (mHasFramebufferObject)
 	{
 		llinfos << "initExtensions() FramebufferObject-related procs..." << llendl;
-		glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glIsRenderbufferEXT");
-		glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBindRenderbufferEXT");
-		glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteRenderbuffersEXT");
-		glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenRenderbuffersEXT");
-		glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageEXT");
-		glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGetRenderbufferParameterivEXT");
-		glIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glIsFramebufferEXT");
-		glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBindFramebufferEXT");
-		glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteFramebuffersEXT");
-		glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenFramebuffersEXT");
-		glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glCheckFramebufferStatusEXT");
-		glFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture1DEXT");
-		glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture2DEXT");
-		glFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture3DEXT");
-		glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferRenderbufferEXT");
-		glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferAttachmentParameterivEXT");
-		glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenerateMipmapEXT");
-	}
-	if (mHasFramebufferMultisample)
-	{
-		glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageMultisampleEXT");
-		glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBlitFramebufferEXT");
+		glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) GLH_EXT_GET_PROC_ADDRESS("glIsRenderbuffer");
+		glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) GLH_EXT_GET_PROC_ADDRESS("glBindRenderbuffer");
+		glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteRenderbuffers");
+		glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) GLH_EXT_GET_PROC_ADDRESS("glGenRenderbuffers");
+		glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorage");
+		glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetRenderbufferParameteriv");
+		glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) GLH_EXT_GET_PROC_ADDRESS("glIsFramebuffer");
+		glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) GLH_EXT_GET_PROC_ADDRESS("glBindFramebuffer");
+		glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteFramebuffers");
+		glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) GLH_EXT_GET_PROC_ADDRESS("glGenFramebuffers");
+		glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) GLH_EXT_GET_PROC_ADDRESS("glCheckFramebufferStatus");
+		glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture1D");
+		glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture2D");
+		glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture3D");
+		glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferRenderbuffer");
+		glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferAttachmentParameteriv");
+		glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) GLH_EXT_GET_PROC_ADDRESS("glGenerateMipmap");
+		glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) GLH_EXT_GET_PROC_ADDRESS("glBlitFramebuffer");
+		glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageMultisample");
+		glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTextureLayer");
 	}
 	if (mHasDrawBuffers)
 	{
@@ -1874,12 +1891,17 @@ void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor
 	}
 }
 
-LLGLUserClipPlane::LLGLUserClipPlane(const LLPlane& p, const glh::matrix4f& modelview, const glh::matrix4f& projection)
+LLGLUserClipPlane::LLGLUserClipPlane(const LLPlane& p, const glh::matrix4f& modelview, const glh::matrix4f& projection, bool apply)
 {
-	mModelview = modelview;
-	mProjection = projection;
+	mApply = apply;
 
-	setPlane(p.mV[0], p.mV[1], p.mV[2], p.mV[3]);
+	if (mApply)
+	{
+		mModelview = modelview;
+		mProjection = projection;
+
+		setPlane(p[0], p[1], p[2], p[3]);
+	}
 }
 
 void LLGLUserClipPlane::setPlane(F32 a, F32 b, F32 c, F32 d)
@@ -1910,9 +1932,12 @@ void LLGLUserClipPlane::setPlane(F32 a, F32 b, F32 c, F32 d)
 
 LLGLUserClipPlane::~LLGLUserClipPlane()
 {
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
+	if (mApply)
+	{
+		glMatrixMode(GL_PROJECTION);
+		glPopMatrix();
+		glMatrixMode(GL_MODELVIEW);
+	}
 }
 
 LLGLNamePool::LLGLNamePool()
@@ -2093,11 +2118,14 @@ void LLGLDepthTest::checkState()
 	}
 }
 
-LLGLSquashToFarClip::LLGLSquashToFarClip(glh::matrix4f P)
+LLGLSquashToFarClip::LLGLSquashToFarClip(glh::matrix4f P, U32 layer)
 {
+
+	F32 depth = 0.99999f - 0.0001f * layer;
+
 	for (U32 i = 0; i < 4; i++)
 	{
-		P.element(2, i) = P.element(3, i) * 0.99999f;
+		P.element(2, i) = P.element(3, i) * depth;
 	}
 
 	glMatrixMode(GL_PROJECTION);
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index 51b0a1e45fb8c6fa709c159fa842b550dba8ae5f..3d002fd8c459f735473cdce459a98afabced370c 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -77,11 +77,12 @@ class LLGLManager
 
 	// Extensions used by everyone
 	BOOL mHasMultitexture;
+	BOOL mHasATIMemInfo;
+	BOOL mHasNVXMemInfo;
 	S32	 mNumTextureUnits;
 	BOOL mHasMipMapGeneration;
 	BOOL mHasCompressedTextures;
 	BOOL mHasFramebufferObject;
-	BOOL mHasFramebufferMultisample;
 	BOOL mHasBlendFuncSeparate;
 	
 	// ARB Extensions
@@ -91,6 +92,7 @@ class LLGLManager
 	BOOL mHasVertexShader;
 	BOOL mHasFragmentShader;
 	BOOL mHasOcclusionQuery;
+	BOOL mHasOcclusionQuery2;
 	BOOL mHasPointParameters;
 	BOOL mHasDrawBuffers;
 	BOOL mHasDepthClamp;
@@ -300,12 +302,14 @@ class LLGLUserClipPlane
 {
 public:
 	
-	LLGLUserClipPlane(const LLPlane& plane, const glh::matrix4f& modelview, const glh::matrix4f& projection);
+	LLGLUserClipPlane(const LLPlane& plane, const glh::matrix4f& modelview, const glh::matrix4f& projection, bool apply = true);
 	~LLGLUserClipPlane();
 
 	void setPlane(F32 a, F32 b, F32 c, F32 d);
 
 private:
+	bool mApply;
+
 	glh::matrix4f mProjection;
 	glh::matrix4f mModelview;
 };
@@ -321,7 +325,7 @@ class LLGLUserClipPlane
 class LLGLSquashToFarClip
 {
 public:
-	LLGLSquashToFarClip(glh::matrix4f projection);
+	LLGLSquashToFarClip(glh::matrix4f projection, U32 layer = 0);
 	~LLGLSquashToFarClip();
 };
 
@@ -419,4 +423,67 @@ extern BOOL gClothRipple;
 extern BOOL gHeadlessClient;
 extern BOOL gGLActive;
 
+// Deal with changing glext.h definitions for newer SDK versions, specifically
+// with MAC OSX 10.5 -> 10.6
+
+
+#ifndef GL_DEPTH_ATTACHMENT
+#define GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_EXT
+#endif
+
+#ifndef GL_STENCIL_ATTACHMENT
+#define GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT_EXT
+#endif
+
+#ifndef GL_FRAMEBUFFER
+#define GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
+#define GL_DRAW_FRAMEBUFFER GL_DRAW_FRAMEBUFFER_EXT
+#define GL_READ_FRAMEBUFFER GL_READ_FRAMEBUFFER_EXT
+#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT
+#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_EXT
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT
+#define glGenFramebuffers glGenFramebuffersEXT
+#define glBindFramebuffer glBindFramebufferEXT
+#define glCheckFramebufferStatus glCheckFramebufferStatusEXT
+#define glBlitFramebuffer glBlitFramebufferEXT
+#define glDeleteFramebuffers glDeleteFramebuffersEXT
+#define glFramebufferRenderbuffer glFramebufferRenderbufferEXT
+#define glFramebufferTexture2D glFramebufferTexture2DEXT
+#endif
+
+#ifndef GL_RENDERBUFFER
+#define GL_RENDERBUFFER GL_RENDERBUFFER_EXT
+#define glGenRenderbuffers glGenRenderbuffersEXT
+#define glBindRenderbuffer glBindRenderbufferEXT
+#define glRenderbufferStorage glRenderbufferStorageEXT
+#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleEXT
+#define glDeleteRenderbuffers glDeleteRenderbuffersEXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT
+#define GL_COLOR_ATTACHMENT GL_COLOR_ATTACHMENT_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT0
+#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT1
+#define GL_COLOR_ATTACHMENT1 GL_COLOR_ATTACHMENT1_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT2
+#define GL_COLOR_ATTACHMENT2 GL_COLOR_ATTACHMENT2_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT3
+#define GL_COLOR_ATTACHMENT3 GL_COLOR_ATTACHMENT3_EXT
+#endif
+
+
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_EXT
+#endif 
+
 #endif // LL_LLGL_H
diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
index 576969b81ae0f6eeab173375280d7fc91e103470..d8140a124db1d5a02b3cf07518d07031ce2896ff 100644
--- a/indra/llrender/llglheaders.h
+++ b/indra/llrender/llglheaders.h
@@ -1,25 +1,25 @@
-/** 
+/**
  * @file llglheaders.h
  * @brief LLGL definitions
  *
  * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, Linden Research, Inc.
- * 
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation;
  * version 2.1 of the License only.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
+ *
  * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  * $/LicenseInfo$
  */
@@ -449,30 +449,27 @@ extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB;
 //GL_EXT_blend_func_separate
 extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT;
 
-//GL_EXT_framebuffer_object
-extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT;
-extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
-extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT;
-extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT;
-extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT;
-extern PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT;
-extern PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT;
-extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
-extern PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
-extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
-extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
-extern PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT;
-extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
-extern PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT;
-extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT;
-extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT;
-extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT;
-
-// GL_EXT_framebuffer_multisample
-extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
-
-// GL_EXT_framebuffer_blit
-extern PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT;
+//GL_ARB_framebuffer_object
+extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer;
+extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
+extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
+extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
+extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
+extern PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv;
+extern PFNGLISFRAMEBUFFERPROC glIsFramebuffer;
+extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
+extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
+extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
+extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
+extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D;
+extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
+extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D;
+extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
+extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv;
+extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
+extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
+extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
+extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer;
 
 //GL_ARB_draw_buffers
 extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
@@ -651,30 +648,27 @@ extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB;
 //GL_EXT_blend_func_separate
 extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT;
 
-//GL_EXT_framebuffer_object
-extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT;
-extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
-extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT;
-extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT;
-extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT;
-extern PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT;
-extern PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT;
-extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
-extern PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
-extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
-extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
-extern PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT;
-extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
-extern PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT;
-extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT;
-extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT;
-extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT;
-
-// GL_EXT_framebuffer_multisample
-extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
-
-// GL_EXT_framebuffer_blit
-extern PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT;
+//GL_ARB_framebuffer_object
+extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer;
+extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
+extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
+extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
+extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
+extern PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv;
+extern PFNGLISFRAMEBUFFERPROC glIsFramebuffer;
+extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
+extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
+extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
+extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
+extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D;
+extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
+extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D;
+extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
+extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv;
+extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
+extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
+extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
+extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer;
 
 //GL_ARB_draw_buffers
 extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
@@ -697,7 +691,7 @@ extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
 #include <AvailabilityMacros.h>
 
 //GL_EXT_blend_func_separate
-extern void glBlendFuncSeparateEXT(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
+extern void glBlendFuncSeparateEXT(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) ;
 
 // GL_EXT_framebuffer_object
 extern GLboolean glIsRenderbufferEXT(GLuint renderbuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
@@ -718,6 +712,9 @@ extern void glFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenu
 extern void glGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, GLint *params) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
 extern void glGenerateMipmapEXT(GLenum target) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
 
+#ifndef GL_ARB_framebuffer_object
+#define glGenerateMipmap glGenerateMipmapEXT
+#endif
 // GL_ARB_draw_buffers
 extern void glDrawBuffersARB(GLsizei n, const GLenum* bufs) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
 
@@ -840,4 +837,22 @@ extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *);
 #define GL_DEPTH_CLAMP 0x864F
 #endif
 
+//GL_NVX_gpu_memory_info constants
+#ifndef GL_NVX_gpu_memory_info
+#define GL_NVX_gpu_memory_info
+#define	GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX          0x9047
+#define	GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX    0x9048
+#define	GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX  0x9049
+#define	GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX            0x904A
+#define	GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX            0x904B
+#endif
+
+//GL_ATI_meminfo constants
+#ifndef GL_ATI_meminfo
+#define GL_ATI_meminfo
+#define GL_VBO_FREE_MEMORY_ATI                     0x87FB
+#define GL_TEXTURE_FREE_MEMORY_ATI                 0x87FC
+#define GL_RENDERBUFFER_FREE_MEMORY_ATI            0x87FD
+#endif
+
 #endif // LL_LLGLHEADERS_H
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 16534fa9a5e00d0528fb84f9a38825c44d084929..257bcd93802883dec2c2ca149a89fb7008ad110a 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -55,7 +55,7 @@ BOOL shouldChange(const LLVector4& v1, const LLVector4& v2)
 
 LLShaderFeatures::LLShaderFeatures()
 : calculatesLighting(false), isShiny(false), isFullbright(false), hasWaterFog(false),
-hasTransport(false), hasSkinning(false), hasAtmospherics(false), isSpecular(false),
+hasTransport(false), hasSkinning(false), hasObjectSkinning(false), hasAtmospherics(false), isSpecular(false),
 hasGamma(false), hasLighting(false), calculatesAtmospherics(false)
 {
 }
@@ -118,7 +118,7 @@ BOOL LLGLSLShader::createShader(vector<string> * attributes,
 	{
 		GLhandleARB shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second);
 		LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL;
-		if (mShaderLevel > 0)
+		if (shaderhandle > 0)
 		{
 			attachObject(shaderhandle);
 		}
@@ -698,17 +698,46 @@ void LLGLSLShader::uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, c
 
 GLint LLGLSLShader::getUniformLocation(const string& uniform)
 {
+	GLint ret = -1;
 	if (mProgramObject > 0)
 	{
 		std::map<string, GLint>::iterator iter = mUniformMap.find(uniform);
 		if (iter != mUniformMap.end())
 		{
-			llassert(iter->second == glGetUniformLocationARB(mProgramObject, uniform.c_str()));
-			return iter->second;
+			if (gDebugGL)
+			{
+				stop_glerror();
+				if (iter->second != glGetUniformLocationARB(mProgramObject, uniform.c_str()))
+				{
+					llerrs << "Uniform does not match." << llendl;
+				}
+				stop_glerror();
+			}
+			ret = iter->second;
 		}
 	}
 
-	return -1;
+	/*if (gDebugGL)
+	{
+		if (ret == -1 && ret != glGetUniformLocationARB(mProgramObject, uniform.c_str()))
+		{
+			llerrs << "Uniform map invalid." << llendl;
+		}
+	}*/
+
+	return ret;
+}
+
+GLint LLGLSLShader::getAttribLocation(U32 attrib)
+{
+	if (attrib < mAttribute.size())
+	{
+		return mAttribute[attrib];
+	}
+	else
+	{
+		return -1;
+	}
 }
 
 void LLGLSLShader::uniform1i(const string& uniform, GLint v)
@@ -882,7 +911,9 @@ void LLGLSLShader::uniformMatrix4fv(const string& uniform, U32 count, GLboolean
 				
 	if (location >= 0)
 	{
+		stop_glerror();
 		glUniformMatrix4fvARB(location, count, transpose, v);
+		stop_glerror();
 	}
 }
 
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index c11bd507165c840dfcf68e0982f3e1e70d4ba098..d46ddbbe183992ab791626a227e8792b67c82912 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -42,6 +42,7 @@ class LLShaderFeatures
 	bool hasWaterFog; // implies no gamma
 	bool hasTransport; // implies no lighting (it's possible to have neither though)
 	bool hasSkinning;	
+	bool hasObjectSkinning;
 	bool hasAtmospherics;
 	bool hasGamma;
 
@@ -103,7 +104,7 @@ class LLGLSLShader
 	void vertexAttrib4fv(U32 index, GLfloat* v);
 	
 	GLint getUniformLocation(const std::string& uniform);
-	
+	GLint getAttribLocation(U32 attrib);
 	GLint mapUniformTextureChannel(GLint location, GLenum type);
 	
 
diff --git a/indra/llrender/llglstates.h b/indra/llrender/llglstates.h
index d5a29dcd0ce495f408f3902db7c3b56eb41f16f7..e26aead676fe6520cb0256a501387bd37c62c1d4 100644
--- a/indra/llrender/llglstates.h
+++ b/indra/llrender/llglstates.h
@@ -238,9 +238,11 @@ class LLGLSTracker
 class LLGLSSpecular
 {
 public:
+	F32 mShininess;
 	LLGLSSpecular(const LLColor4& color, F32 shininess)
 	{
-		if (shininess > 0.0f)
+		mShininess = shininess;
+		if (mShininess > 0.0f)
 		{
 			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color.mV);
 			S32 shiny = (S32)(shininess*128.f);
@@ -250,32 +252,14 @@ class LLGLSSpecular
 	}
 	~LLGLSSpecular()
 	{
-		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, LLColor4(0.f,0.f,0.f,0.f).mV);
-		glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 0);
+		if (mShininess > 0.f)
+		{
+			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, LLColor4(0.f,0.f,0.f,0.f).mV);
+			glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 0);
+		}
 	}
 };
 
 //----------------------------------------------------------------------------
 
-
-class LLGLSBlendFunc : public LLGLSPipeline {
-protected:
-	GLint mSavedSrc, mSavedDst;
-	LLGLEnable mBlend;
-
-public:
-	LLGLSBlendFunc(GLenum srcFunc, GLenum dstFunc) :
-		mBlend(GL_BLEND)
-	{
-		glGetIntegerv(GL_BLEND_SRC, &mSavedSrc);
-		glGetIntegerv(GL_BLEND_DST, &mSavedDst);
-		glBlendFunc(srcFunc, dstFunc);
-	}
-
-	~LLGLSBlendFunc(void) {
-		glBlendFunc(mSavedSrc, mSavedDst);
-	}
-};
-
-
 #endif
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index d4ffd6f88e630cd94a32274606e2e06569f193dc..d408077c683d48f97cd042b8bb366061ebacd432 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1710,6 +1710,7 @@ void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h)
 				sample[asum/(16*4)] += 4;
 			}
 			
+			
 			rowstart += 2 * w * mAlphaStride;
 		}
 		length *= 2; // we sampled everything twice, essentially
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 8eb160f4e78ea4402cbb2120d9d4efa421fe2c0b..49e10c47902911dc082f79f96bbf16df966dbe63 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -47,6 +47,7 @@ U32 LLRender::sUICalls = 0;
 U32 LLRender::sUIVerts = 0;
 
 static const U32 LL_NUM_TEXTURE_LAYERS = 16; 
+static const U32 LL_NUM_LIGHT_UNITS = 8;
 
 static GLenum sGLTextureType[] =
 {
@@ -747,6 +748,130 @@ void LLTexUnit::debugTextureUnit(void)
 	}
 }
 
+LLLightState::LLLightState(S32 index)
+: mIndex(index),
+  mEnabled(false),
+  mConstantAtten(1.f),
+  mLinearAtten(0.f),
+  mQuadraticAtten(0.f),
+  mSpotExponent(0.f),
+  mSpotCutoff(180.f)
+{
+	if (mIndex == 0)
+	{
+		mDiffuse.set(1,1,1,1);
+		mSpecular.set(1,1,1,1);
+	}
+
+	mAmbient.set(0,0,0,1);
+	mPosition.set(0,0,1,0);
+	mSpotDirection.set(0,0,-1);
+
+}
+
+void LLLightState::enable()
+{
+	if (!mEnabled)
+	{
+		glEnable(GL_LIGHT0+mIndex);
+		mEnabled = true;
+	}
+}
+
+void LLLightState::disable()
+{
+	if (mEnabled)
+	{
+		glDisable(GL_LIGHT0+mIndex);
+		mEnabled = false;
+	}
+}
+
+void LLLightState::setDiffuse(const LLColor4& diffuse)
+{
+	if (mDiffuse != diffuse)
+	{
+		mDiffuse = diffuse;
+		glLightfv(GL_LIGHT0+mIndex, GL_DIFFUSE, mDiffuse.mV);
+	}
+}
+
+void LLLightState::setAmbient(const LLColor4& ambient)
+{
+	if (mAmbient != ambient)
+	{
+		mAmbient = ambient;
+		glLightfv(GL_LIGHT0+mIndex, GL_AMBIENT, mAmbient.mV);
+	}
+}
+
+void LLLightState::setSpecular(const LLColor4& specular)
+{
+	if (mSpecular != specular)
+	{
+		mSpecular = specular;
+		glLightfv(GL_LIGHT0+mIndex, GL_SPECULAR, mSpecular.mV);
+	}
+}
+
+void LLLightState::setPosition(const LLVector4& position)
+{
+	//always set position because modelview matrix may have changed
+	mPosition = position;
+	glLightfv(GL_LIGHT0+mIndex, GL_POSITION, mPosition.mV);
+}
+
+void LLLightState::setConstantAttenuation(const F32& atten)
+{
+	if (mConstantAtten != atten)
+	{
+		mConstantAtten = atten;
+		glLightf(GL_LIGHT0+mIndex, GL_CONSTANT_ATTENUATION, atten);
+	}
+}
+
+void LLLightState::setLinearAttenuation(const F32& atten)
+{
+	if (mLinearAtten != atten)
+	{
+		mLinearAtten = atten;
+		glLightf(GL_LIGHT0+mIndex, GL_LINEAR_ATTENUATION, atten);
+	}
+}
+
+void LLLightState::setQuadraticAttenuation(const F32& atten)
+{
+	if (mQuadraticAtten != atten)
+	{
+		mQuadraticAtten = atten;
+		glLightf(GL_LIGHT0+mIndex, GL_QUADRATIC_ATTENUATION, atten);
+	}
+}
+
+void LLLightState::setSpotExponent(const F32& exponent)
+{
+	if (mSpotExponent != exponent)
+	{
+		mSpotExponent = exponent;
+		glLightf(GL_LIGHT0+mIndex, GL_SPOT_EXPONENT, exponent);
+	}
+}
+
+void LLLightState::setSpotCutoff(const F32& cutoff)
+{
+	if (mSpotCutoff != cutoff)
+	{
+		mSpotCutoff = cutoff;
+		glLightf(GL_LIGHT0+mIndex, GL_SPOT_CUTOFF, cutoff);
+	}
+}
+
+void LLLightState::setSpotDirection(const LLVector3& direction)
+{
+	//always set direction because modelview matrix may have changed
+	mSpotDirection = direction;
+	glLightfv(GL_LIGHT0+mIndex, GL_SPOT_DIRECTION, direction.mV);
+}
 
 LLRender::LLRender()
   : mDirty(false),
@@ -768,6 +893,11 @@ LLRender::LLRender()
 	}
 	mDummyTexUnit = new LLTexUnit(-1);
 
+	for (U32 i = 0; i < LL_NUM_LIGHT_UNITS; ++i)
+	{
+		mLightState.push_back(new LLLightState(i));
+	}
+
 	for (U32 i = 0; i < 4; i++)
 	{
 		mCurrColorMask[i] = true;
@@ -795,6 +925,12 @@ void LLRender::shutdown()
 	mTexUnits.clear();
 	delete mDummyTexUnit;
 	mDummyTexUnit = NULL;
+
+	for (U32 i = 0; i < mLightState.size(); ++i)
+	{
+		delete mLightState[i];
+	}
+	mLightState.clear();
 }
 
 void LLRender::refreshState(void)
@@ -898,7 +1034,7 @@ LLVector3 LLRender::getUITranslation()
 {
 	if (mUIOffset.empty())
 	{
-		return LLVector3::zero;
+		return LLVector3(0,0,0);
 	}
 	return mUIOffset.back();
 }
@@ -907,7 +1043,7 @@ LLVector3 LLRender::getUIScale()
 {
 	if (mUIScale.empty())
 	{
-		return LLVector3(1.f, 1.f, 1.f);
+		return LLVector3(1,1,1);
 	}
 	return mUIScale.back();
 }
@@ -932,15 +1068,21 @@ void LLRender::setColorMask(bool writeColorR, bool writeColorG, bool writeColorB
 {
 	flush();
 
-	mCurrColorMask[0] = writeColorR;
-	mCurrColorMask[1] = writeColorG;
-	mCurrColorMask[2] = writeColorB;
-	mCurrColorMask[3] = writeAlpha;
+	if (mCurrColorMask[0] != writeColorR ||
+		mCurrColorMask[1] != writeColorG ||
+		mCurrColorMask[2] != writeColorB ||
+		mCurrColorMask[3] != writeAlpha)
+	{
+		mCurrColorMask[0] = writeColorR;
+		mCurrColorMask[1] = writeColorG;
+		mCurrColorMask[2] = writeColorB;
+		mCurrColorMask[3] = writeAlpha;
 
-	glColorMask(writeColorR ? GL_TRUE : GL_FALSE, 
-				writeColorG ? GL_TRUE : GL_FALSE,
-				writeColorB ? GL_TRUE : GL_FALSE,
-				writeAlpha ? GL_TRUE : GL_FALSE);
+		glColorMask(writeColorR ? GL_TRUE : GL_FALSE, 
+					writeColorG ? GL_TRUE : GL_FALSE,
+					writeColorB ? GL_TRUE : GL_FALSE,
+					writeAlpha ? GL_TRUE : GL_FALSE);
+	}
 }
 
 void LLRender::setSceneBlendType(eBlendType type)
@@ -978,15 +1120,19 @@ void LLRender::setAlphaRejectSettings(eCompareFunc func, F32 value)
 {
 	flush();
 
-	mCurrAlphaFunc = func;
-	mCurrAlphaFuncVal = value;
-	if (func == CF_DEFAULT)
-	{
-		glAlphaFunc(GL_GREATER, 0.01f);
-	} 
-	else
+	if (mCurrAlphaFunc != func ||
+		mCurrAlphaFuncVal != value)
 	{
-		glAlphaFunc(sGLCompareFunc[func], value);
+		mCurrAlphaFunc = func;
+		mCurrAlphaFuncVal = value;
+		if (func == CF_DEFAULT)
+		{
+			glAlphaFunc(GL_GREATER, 0.01f);
+		} 
+		else
+		{
+			glAlphaFunc(sGLCompareFunc[func], value);
+		}
 	}
 }
 
@@ -1045,6 +1191,16 @@ LLTexUnit* LLRender::getTexUnit(U32 index)
 	}
 }
 
+LLLightState* LLRender::getLight(U32 index)
+{
+	if (index < mLightState.size())
+	{
+		return mLightState[index];
+	}
+	
+	return NULL;
+}
+
 bool LLRender::verifyTexUnitActive(U32 unitToVerify)
 {
 	if (mCurrTextureUnitIndex == unitToVerify)
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index 2767aa64a8a675bf891e11a4b1ee3928f2938e98..7ba14f7b405376f77060bfb331ca18b0bb31b27a 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -37,6 +37,7 @@
 #include "v2math.h"
 #include "v3math.h"
 #include "v4coloru.h"
+#include "v4math.h"
 #include "llstrider.h"
 #include "llpointer.h"
 #include "llglheaders.h"
@@ -212,6 +213,41 @@ class LLTexUnit
 	void setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2, bool isAlpha = false);
 };
 
+class LLLightState
+{
+public:
+	LLLightState(S32 index);
+
+	void enable();
+	void disable();
+	void setDiffuse(const LLColor4& diffuse);
+	void setAmbient(const LLColor4& ambient);
+	void setSpecular(const LLColor4& specular);
+	void setPosition(const LLVector4& position);
+	void setConstantAttenuation(const F32& atten);
+	void setLinearAttenuation(const F32& atten);
+	void setQuadraticAttenuation(const F32& atten);
+	void setSpotExponent(const F32& exponent);
+	void setSpotCutoff(const F32& cutoff);
+	void setSpotDirection(const LLVector3& direction);
+
+protected:
+	S32 mIndex;
+	bool mEnabled;
+	LLColor4 mDiffuse;
+	LLColor4 mAmbient;
+	LLColor4 mSpecular;
+	LLVector4 mPosition;
+	LLVector3 mSpotDirection;
+
+	F32 mConstantAtten;
+	F32 mLinearAtten;
+	F32 mQuadraticAtten;
+
+	F32 mSpotExponent;
+	F32 mSpotCutoff;
+};
+
 class LLRender
 {
 	friend class LLTexUnit;
@@ -327,6 +363,8 @@ class LLRender
 	void blendFunc(eBlendFactor color_sfactor, eBlendFactor color_dfactor,
 		       eBlendFactor alpha_sfactor, eBlendFactor alpha_dfactor);
 
+	LLLightState* getLight(U32 index);
+
 	LLTexUnit* getTexUnit(U32 index);
 
 	U32	getCurrentTexUnitIndex(void) const { return mCurrTextureUnitIndex; }
@@ -363,6 +401,7 @@ class LLRender
 	LLStrider<LLColor4U>		mColorsp;
 	std::vector<LLTexUnit*>		mTexUnits;
 	LLTexUnit*			mDummyTexUnit;
+	std::vector<LLLightState*> mLightState;
 
 	eBlendFactor mCurrBlendColorSFactor;
 	eBlendFactor mCurrBlendColorDFactor;
diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index 7205210fccddf0919f20ebf23e937677175b025f..cd2556d435526757eb4a652244eefd7c53940f92 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -38,10 +38,10 @@ void check_framebuffer_status()
 {
 	if (gDebugGL)
 	{
-		GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+		GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
 		switch (status)
 		{
-		case GL_FRAMEBUFFER_COMPLETE_EXT:
+		case GL_FRAMEBUFFER_COMPLETE:
 			break;
 		default:
 			ll_fail("check_framebuffer_status failed");	
@@ -50,7 +50,7 @@ void check_framebuffer_status()
 	}
 }
 
-BOOL LLRenderTarget::sUseFBO = FALSE;
+bool LLRenderTarget::sUseFBO = false;
 
 LLRenderTarget::LLRenderTarget() :
 	mResX(0),
@@ -59,8 +59,8 @@ LLRenderTarget::LLRenderTarget() :
 	mFBO(0),
 	mDepth(0),
 	mStencil(0),
-	mUseDepth(FALSE),
-	mRenderDepth(FALSE),
+	mUseDepth(false),
+	mRenderDepth(false),
 	mUsage(LLTexUnit::TT_TEXTURE),
 	mSamples(0),
 	mSampleBuffer(NULL)
@@ -78,7 +78,7 @@ void LLRenderTarget::setSampleBuffer(LLMultisampleBuffer* buffer)
 	mSampleBuffer = buffer;
 }
 
-void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo)
+void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo)
 {
 	stop_glerror();
 	mResX = resx;
@@ -99,24 +99,24 @@ void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOO
 			stop_glerror();
 		}
 
-		glGenFramebuffersEXT(1, (GLuint *) &mFBO);
+		glGenFramebuffers(1, (GLuint *) &mFBO);
 
 		if (mDepth)
 		{
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+			glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
 			if (mStencil)
 			{
-				glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth);
+				glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth);
 				stop_glerror();
-				glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth);
+				glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth);
 				stop_glerror();
 			}
 			else
 			{
-				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
+				glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
 				stop_glerror();
 			}
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+			glBindFramebuffer(GL_FRAMEBUFFER, 0);
 		}
 		
 		stop_glerror();
@@ -168,14 +168,14 @@ void LLRenderTarget::addColorAttachment(U32 color_fmt)
 	}
 	if (mFBO)
 	{
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
-		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+offset,
+		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset,
 			LLTexUnit::getInternalType(mUsage), tex, 0);
 			stop_glerror();
 
 		check_framebuffer_status();
 		
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 	}
 
 	mTex.push_back(tex);
@@ -187,10 +187,10 @@ void LLRenderTarget::allocateDepth()
 	if (mStencil)
 	{
 		//use render buffers where stencil buffers are in play
-		glGenRenderbuffersEXT(1, (GLuint *) &mDepth);
-		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, mDepth);
-		glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, mResX, mResY);
-		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+		glGenRenderbuffers(1, (GLuint *) &mDepth);
+		glBindRenderbuffer(GL_RENDERBUFFER, mDepth);
+		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mResX, mResY);
+		glBindRenderbuffer(GL_RENDERBUFFER, 0);
 	}
 	else
 	{
@@ -198,7 +198,7 @@ void LLRenderTarget::allocateDepth()
 		gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
 		U32 internal_type = LLTexUnit::getInternalType(mUsage);
 		gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
-		LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32_ARB, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+		LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
 	}
 }
 
@@ -209,54 +209,48 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target)
 		llerrs << "Cannot share depth buffer between non FBO render targets." << llendl;
 	}
 
+	if (target.mDepth)
+	{
+		llerrs << "Attempting to override existing depth buffer.  Detach existing buffer first." << llendl;
+	}
+
+	if (target.mUseDepth)
+	{
+		llerrs << "Attempting to override existing shared depth buffer. Detach existing buffer first." << llendl;
+	}
+
 	if (mDepth)
 	{
 		stop_glerror();
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, target.mFBO);
+		glBindFramebuffer(GL_FRAMEBUFFER, target.mFBO);
 		stop_glerror();
 
 		if (mStencil)
 		{
-			glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth);
+			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth);
 			stop_glerror();
-			glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth);			
+			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth);			
 			stop_glerror();
+			target.mStencil = true;
 		}
 		else
 		{
-			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
+			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
 			stop_glerror();
-			if (mStencil)
-			{
-				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
-				stop_glerror();
-			}
 		}
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
-		target.mUseDepth = TRUE;
+		target.mUseDepth = true;
 	}
 }
 
 void LLRenderTarget::release()
 {
-	if (mFBO)
-	{
-		glDeleteFramebuffersEXT(1, (GLuint *) &mFBO);
-		mFBO = 0;
-	}
-
-	if (mTex.size() > 0)
-	{
-		LLImageGL::deleteTextures(mTex.size(), &mTex[0]);
-		mTex.clear();
-	}
-
 	if (mDepth)
 	{
 		if (mStencil)
 		{
-			glDeleteRenderbuffersEXT(1, (GLuint*) &mDepth);
+			glDeleteRenderbuffers(1, (GLuint*) &mDepth);
 			stop_glerror();
 		}
 		else
@@ -266,6 +260,33 @@ void LLRenderTarget::release()
 		}
 		mDepth = 0;
 	}
+	else if (mUseDepth && mFBO)
+	{ //detach shared depth buffer
+		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
+		if (mStencil)
+		{ //attached as a renderbuffer
+			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
+			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+			mStencil = false;
+		}
+		else
+		{ //attached as a texture
+			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), 0, 0);
+		}
+		mUseDepth = false;
+	}
+
+	if (mFBO)
+	{
+		glDeleteFramebuffers(1, (GLuint *) &mFBO);
+		mFBO = 0;
+	}
+
+	if (mTex.size() > 0)
+	{
+		LLImageGL::deleteTextures(mTex.size(), &mTex[0]);
+		mTex.clear();
+	}
 
 	mSampleBuffer = NULL;
 	sBoundTarget = NULL;
@@ -283,14 +304,14 @@ void LLRenderTarget::bindTarget()
 		}
 		else
 		{
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+			glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
 			stop_glerror();
 			if (gGLManager.mHasDrawBuffers)
 			{ //setup multiple render targets
-				GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0_EXT,
-										GL_COLOR_ATTACHMENT1_EXT,
-										GL_COLOR_ATTACHMENT2_EXT,
-										GL_COLOR_ATTACHMENT3_EXT};
+				GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,
+										GL_COLOR_ATTACHMENT1,
+										GL_COLOR_ATTACHMENT2,
+										GL_COLOR_ATTACHMENT3};
 				glDrawBuffersARB(mTex.size(), drawbuffers);
 			}
 			
@@ -315,7 +336,7 @@ void LLRenderTarget::unbindTarget()
 {
 	if (gGLManager.mHasFramebufferObject)
 	{
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 	}
 	sBoundTarget = NULL;
 }
@@ -349,19 +370,19 @@ U32 LLRenderTarget::getTexture(U32 attachment) const
 	{
 		llerrs << "Invalid attachment index." << llendl;
 	}
+	if (mTex.empty())
+	{
+		return 0;
+	}
 	return mTex[attachment];
 }
 
 void LLRenderTarget::bindTexture(U32 index, S32 channel)
 {
-	if (index > mTex.size()-1)
-	{
-		llerrs << "Invalid attachment index." << llendl;
-	}
-	gGL.getTexUnit(channel)->bindManual(mUsage, mTex[index]);
+	gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index));
 }
 
-void LLRenderTarget::flush(BOOL fetch_depth)
+void LLRenderTarget::flush(bool fetch_depth)
 {
 	gGL.flush();
 	if (!mFBO)
@@ -377,7 +398,7 @@ void LLRenderTarget::flush(BOOL fetch_depth)
 			}
 
 			gGL.getTexUnit(0)->bind(this);
-			glCopyTexImage2D(LLTexUnit::getInternalType(mUsage), 0, GL_DEPTH24_STENCIL8_EXT, 0, 0, mResX, mResY, 0);
+			glCopyTexImage2D(LLTexUnit::getInternalType(mUsage), 0, GL_DEPTH24_STENCIL8, 0, 0, mResX, mResY, 0);
 		}
 
 		gGL.getTexUnit(0)->disable();
@@ -386,55 +407,59 @@ void LLRenderTarget::flush(BOOL fetch_depth)
 	{
 		stop_glerror();
 
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
 		stop_glerror();
 	
 		if (mSampleBuffer)
 		{
-			LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+			LLGLEnable multisample(GL_MULTISAMPLE);
 			stop_glerror();
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+			glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
 			stop_glerror();
 			check_framebuffer_status();
-			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, mSampleBuffer->mFBO);
+			glBindFramebuffer(GL_READ_FRAMEBUFFER, mSampleBuffer->mFBO);
 			check_framebuffer_status();
 			
 			stop_glerror();
-			glBlitFramebufferEXT(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
+			glBlitFramebuffer(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
 			stop_glerror();		
 
 			if (mTex.size() > 1)
 			{		
 				for (U32 i = 1; i < mTex.size(); ++i)
 				{
-					glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+					glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
 										LLTexUnit::getInternalType(mUsage), mTex[i], 0);
 					stop_glerror();
-					glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, mSampleBuffer->mTex[i]);
+					glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mSampleBuffer->mTex[i]);
 					stop_glerror();
-					glBlitFramebufferEXT(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT, GL_NEAREST);		
+					glBlitFramebuffer(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT, GL_NEAREST);		
 					stop_glerror();
 				}
 
 				for (U32 i = 0; i < mTex.size(); ++i)
 				{
-					glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i,
+					glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i,
 										LLTexUnit::getInternalType(mUsage), mTex[i], 0);
 					stop_glerror();
-					glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_RENDERBUFFER_EXT, mSampleBuffer->mTex[i]);
+					glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, mSampleBuffer->mTex[i]);
 					stop_glerror();
 				}
 			}
 		}
 
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 	}
 }
 
 void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
 						S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter)
 {
+	GLboolean write_depth = mask & GL_DEPTH_BUFFER_BIT ? TRUE : FALSE;
+
+	LLGLDepthTest depth(write_depth, write_depth);
+
 	gGL.flush();
 	if (!source.mFBO || !mFBO)
 	{
@@ -451,25 +476,25 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0,
 		{
 			stop_glerror();
 		
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, source.mFBO);
+			glBindFramebuffer(GL_FRAMEBUFFER, source.mFBO);
 			gGL.getTexUnit(0)->bind(this, true);
 			stop_glerror();
 			glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1);
 			stop_glerror();
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+			glBindFramebuffer(GL_FRAMEBUFFER, 0);
 			stop_glerror();
 		}
 		else
 		{
-			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, source.mFBO);
+			glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO);
 			stop_glerror();
-			glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, mFBO);
+			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO);
 			stop_glerror();
 			check_framebuffer_status();
 			stop_glerror();
-			glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+			glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
 			stop_glerror();
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+			glBindFramebuffer(GL_FRAMEBUFFER, 0);
 			stop_glerror();
 		}
 	}
@@ -484,22 +509,26 @@ void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0
 		llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl;
 	}
 	{
-		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, source.mFBO);
+		GLboolean write_depth = mask & GL_DEPTH_BUFFER_BIT ? TRUE : FALSE;
+
+		LLGLDepthTest depth(write_depth, write_depth);
+		
+		glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO);
 		stop_glerror();
-		glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 		stop_glerror();
 		check_framebuffer_status();
 		stop_glerror();
-		glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+		glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
 		stop_glerror();
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 		stop_glerror();
 	}
 }
 
-BOOL LLRenderTarget::isComplete() const
+bool LLRenderTarget::isComplete() const
 {
-	return (!mTex.empty() || mDepth) ? TRUE : FALSE;
+	return (!mTex.empty() || mDepth) ? true : false;
 }
 
 void LLRenderTarget::getViewport(S32* viewport)
@@ -520,26 +549,26 @@ LLMultisampleBuffer::LLMultisampleBuffer()
 
 LLMultisampleBuffer::~LLMultisampleBuffer()
 {
-	releaseSampleBuffer();
+	release();
 }
 
-void LLMultisampleBuffer::releaseSampleBuffer()
+void LLMultisampleBuffer::release()
 {
 	if (mFBO)
 	{
-		glDeleteFramebuffersEXT(1, (GLuint *) &mFBO);
+		glDeleteFramebuffers(1, (GLuint *) &mFBO);
 		mFBO = 0;
 	}
 
 	if (mTex.size() > 0)
 	{
-		glDeleteRenderbuffersEXT(mTex.size(), (GLuint *) &mTex[0]);
+		glDeleteRenderbuffers(mTex.size(), (GLuint *) &mTex[0]);
 		mTex.clear();
 	}
 
 	if (mDepth)
 	{
-		glDeleteRenderbuffersEXT(1, (GLuint *) &mDepth);
+		glDeleteRenderbuffers(1, (GLuint *) &mDepth);
 		mDepth = 0;
 	}
 }
@@ -556,13 +585,13 @@ void LLMultisampleBuffer::bindTarget(LLRenderTarget* ref)
 		ref = this;
 	}
 
-	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+	glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
 	if (gGLManager.mHasDrawBuffers)
 	{ //setup multiple render targets
-		GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0_EXT,
-								GL_COLOR_ATTACHMENT1_EXT,
-								GL_COLOR_ATTACHMENT2_EXT,
-								GL_COLOR_ATTACHMENT3_EXT};
+		GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,
+								GL_COLOR_ATTACHMENT1,
+								GL_COLOR_ATTACHMENT2,
+								GL_COLOR_ATTACHMENT3};
 		glDrawBuffersARB(ref->mTex.size(), drawbuffers);
 	}
 
@@ -573,12 +602,12 @@ void LLMultisampleBuffer::bindTarget(LLRenderTarget* ref)
 	sBoundTarget = this;
 }
 
-void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil,  LLTexUnit::eTextureType usage, BOOL use_fbo )
+void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil,  LLTexUnit::eTextureType usage, bool use_fbo )
 {
 	allocate(resx,resy,color_fmt,depth,stencil,usage,use_fbo,2);
 }
 
-void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil,  LLTexUnit::eTextureType usage, BOOL use_fbo, U32 samples )
+void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil,  LLTexUnit::eTextureType usage, bool use_fbo, U32 samples )
 {
 	stop_glerror();
 	mResX = resx;
@@ -588,12 +617,7 @@ void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth
 	mUseDepth = depth;
 	mStencil = stencil;
 
-	releaseSampleBuffer();
-
-	if (!gGLManager.mHasFramebufferMultisample)
-	{
-		llerrs << "Attempting to allocate unsupported render target type!" << llendl;
-	}
+	release();
 
 	mSamples = samples;
 	
@@ -614,23 +638,21 @@ void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth
 			stop_glerror();
 		}
 
-		glGenFramebuffersEXT(1, (GLuint *) &mFBO);
+		glGenFramebuffers(1, (GLuint *) &mFBO);
 
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
 
 		if (mDepth)
 		{
-			glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth);
+			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth);
 			if (mStencil)
 			{
-				glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth);			
+				glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth);			
 			}
-			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
 		}
-		
+	
 		stop_glerror();
-
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 		stop_glerror();
 	}
 
@@ -652,30 +674,28 @@ void LLMultisampleBuffer::addColorAttachment(U32 color_fmt)
 	}
 
 	U32 tex;
-	glGenRenderbuffersEXT(1, &tex);
+	glGenRenderbuffers(1, &tex);
 	
-	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, tex);
-	glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, color_fmt, mResX, mResY);
+	glBindRenderbuffer(GL_RENDERBUFFER, tex);
+	glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, color_fmt, mResX, mResY);
 	stop_glerror();
 
 	if (mFBO)
 	{
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
-		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+offset, GL_RENDERBUFFER_EXT, tex);
+		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset, GL_RENDERBUFFER, tex);
 		stop_glerror();
-		GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 		switch (status)
 		{
-		case GL_FRAMEBUFFER_COMPLETE_EXT:
-			break;
-		case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
-			llerrs << "WTF?" << llendl;
+		case GL_FRAMEBUFFER_COMPLETE:
 			break;
 		default:
-			llerrs << "WTF?" << llendl;
+			llerrs << "WTF? " << std::hex << status << llendl;
+			break;
 		}
 
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 	}
 
 	mTex.push_back(tex);
@@ -683,15 +703,15 @@ void LLMultisampleBuffer::addColorAttachment(U32 color_fmt)
 
 void LLMultisampleBuffer::allocateDepth()
 {
-	glGenRenderbuffersEXT(1, (GLuint* ) &mDepth);
-	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, mDepth);
+	glGenRenderbuffers(1, (GLuint* ) &mDepth);
+	glBindRenderbuffer(GL_RENDERBUFFER, mDepth);
 	if (mStencil)
 	{
-		glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, GL_DEPTH24_STENCIL8_EXT, mResX, mResY);	
+		glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, GL_DEPTH24_STENCIL8, mResX, mResY);	
 	}
 	else
 	{
-		glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, GL_DEPTH_COMPONENT16_ARB, mResX, mResY);	
+		glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, GL_DEPTH_COMPONENT16, mResX, mResY);	
 	}
 }
 
diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h
index ae8613d9bec99c57501b803e237a4be917120134..12dd1c8b90f85a734e9a108226a4e85552588798 100644
--- a/indra/llrender/llrendertarget.h
+++ b/indra/llrender/llrendertarget.h
@@ -63,7 +63,7 @@ class LLRenderTarget
 {
 public:
 	//whether or not to use FBO implementation
-	static BOOL sUseFBO; 
+	static bool sUseFBO; 
 
 	LLRenderTarget();
 	virtual ~LLRenderTarget();
@@ -71,7 +71,7 @@ class LLRenderTarget
 	//allocate resources for rendering
 	//must be called before use
 	//multiple calls will release previously allocated resources
-	void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, BOOL use_fbo = FALSE);
+	void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = FALSE);
 
 	//provide this render target with a multisample resource.
 	void setSampleBuffer(LLMultisampleBuffer* buffer);
@@ -88,7 +88,7 @@ class LLRenderTarget
 
 	//free any allocated resources
 	//safe to call redundantly
-	void release();
+	virtual void release();
 
 	//bind target for rendering
 	//applies appropriate viewport
@@ -115,7 +115,7 @@ class LLRenderTarget
 	U32 getTexture(U32 attachment = 0) const;
 
 	U32 getDepth(void) const { return mDepth; }
-	BOOL hasStencil() const { return mStencil; }
+	bool hasStencil() const { return mStencil; }
 
 	void bindTexture(U32 index, S32 channel);
 
@@ -125,7 +125,7 @@ class LLRenderTarget
 	// call bindTarget once, do all your rendering, call flush once
 	// if fetch_depth is TRUE, every effort will be made to copy the depth buffer into 
 	// the current depth texture.  A depth texture will be allocated if needed.
-	void flush(BOOL fetch_depth = FALSE);
+	void flush(bool fetch_depth = FALSE);
 
 	void copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
 						S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter);
@@ -136,7 +136,7 @@ class LLRenderTarget
 	//Returns TRUE if target is ready to be rendered into.
 	//That is, if the target has been allocated with at least
 	//one renderable attachment (i.e. color buffer, depth buffer).
-	BOOL isComplete() const;
+	bool isComplete() const;
 
 	static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; }
 
@@ -147,9 +147,9 @@ class LLRenderTarget
 	std::vector<U32> mTex;
 	U32 mFBO;
 	U32 mDepth;
-	BOOL mStencil;
-	BOOL mUseDepth;
-	BOOL mRenderDepth;
+	bool mStencil;
+	bool mUseDepth;
+	bool mRenderDepth;
 	LLTexUnit::eTextureType mUsage;
 	U32 mSamples;
 	LLMultisampleBuffer* mSampleBuffer;
@@ -164,12 +164,12 @@ class LLMultisampleBuffer : public LLRenderTarget
 	LLMultisampleBuffer();
 	virtual ~LLMultisampleBuffer();
 
-	void releaseSampleBuffer();
+	virtual void release();
 
 	virtual void bindTarget();
 	void bindTarget(LLRenderTarget* ref);
-	virtual void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo);
-	void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo, U32 samples);
+	virtual void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo);
+	void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, U32 samples);
 	virtual void addColorAttachment(U32 color_fmt);
 	virtual void allocateDepth();
 };
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index c859d41e17705298b61aa8a741c6c13edbd40947..98a0a93084e1762281ba02da11ec5daff2490b6e 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -146,6 +146,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
 			return FALSE;
 		}
 	}
+
+	if (features->hasObjectSkinning)
+	{
+		if (!shader->attachObject("avatar/objectSkinV.glsl"))
+		{
+			return FALSE;
+		}
+	}
 	
 	///////////////////////////////////////
 	// Attach Fragment Shader Features Next
@@ -220,7 +228,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
 	else if (features->isFullbright)
 	{
 	
-		if (features->hasWaterFog)
+		if (features->isShiny && features->hasWaterFog)
+		{
+			if (!shader->attachObject("lighting/lightFullbrightShinyWaterF.glsl"))
+			{
+				return FALSE;
+			}
+		}
+		else if (features->hasWaterFog)
 		{
 			if (!shader->attachObject("lighting/lightFullbrightWaterF.glsl"))
 			{
@@ -300,18 +315,21 @@ void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns)
 		}
 		else
 		{
-			LL_DEBUGS("ShaderLoading") << log << LL_ENDL;
+			LL_INFOS("ShaderLoading") << log << LL_ENDL;
 		}
 	}
 }
 
 GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type)
 {
-	GLenum error;
-	error = glGetError();
-	if (error != GL_NO_ERROR)
+	GLenum error = GL_NO_ERROR;
+	if (gDebugGL)
 	{
-		LL_WARNS("ShaderLoading") << "GL ERROR entering loadShaderFile(): " << error << LL_ENDL;
+		error = glGetError();
+		if (error != GL_NO_ERROR)
+		{
+			LL_WARNS("ShaderLoading") << "GL ERROR entering loadShaderFile(): " << error << LL_ENDL;
+		}
 	}
 	
 	LL_DEBUGS("ShaderLoading") << "Loading shader file: " << filename << " class " << shader_level << LL_ENDL;
@@ -366,31 +384,39 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
 
 	//create shader object
 	GLhandleARB ret = glCreateShaderObjectARB(type);
-	error = glGetError();
-	if (error != GL_NO_ERROR)
+	if (gDebugGL)
 	{
-		LL_WARNS("ShaderLoading") << "GL ERROR in glCreateShaderObjectARB: " << error << LL_ENDL;
+		error = glGetError();
+		if (error != GL_NO_ERROR)
+		{
+			LL_WARNS("ShaderLoading") << "GL ERROR in glCreateShaderObjectARB: " << error << LL_ENDL;
+		}
 	}
-	else
+	
+	//load source
+	glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL);
+
+	if (gDebugGL)
 	{
-		//load source
-		glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL);
 		error = glGetError();
 		if (error != GL_NO_ERROR)
 		{
 			LL_WARNS("ShaderLoading") << "GL ERROR in glShaderSourceARB: " << error << LL_ENDL;
 		}
-		else
+	}
+
+	//compile source
+	glCompileShaderARB(ret);
+
+	if (gDebugGL)
+	{
+		error = glGetError();
+		if (error != GL_NO_ERROR)
 		{
-			//compile source
-			glCompileShaderARB(ret);
-			error = glGetError();
-			if (error != GL_NO_ERROR)
-			{
-				LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShaderARB: " << error << LL_ENDL;
-			}
+			LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShaderARB: " << error << LL_ENDL;
 		}
 	}
+		
 	//free memory
 	for (GLuint i = 0; i < count; i++)
 	{
@@ -401,13 +427,16 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
 		//check for errors
 		GLint success = GL_TRUE;
 		glGetObjectParameterivARB(ret, GL_OBJECT_COMPILE_STATUS_ARB, &success);
-		error = glGetError();
-		if (error != GL_NO_ERROR || success == GL_FALSE) 
+		if (gDebugGL || success == GL_FALSE)
 		{
-			//an error occured, print log
-			LL_WARNS("ShaderLoading") << "GLSL Compilation Error: (" << error << ") in " << filename << LL_ENDL;
-			dumpObjectLog(ret);
-			ret = 0;
+			error = glGetError();
+			if (error != GL_NO_ERROR || success == GL_FALSE) 
+			{
+				//an error occured, print log
+				LL_WARNS("ShaderLoading") << "GLSL Compilation Error: (" << error << ") in " << filename << LL_ENDL;
+				dumpObjectLog(ret);
+				ret = 0;
+			}
 		}
 	}
 	else
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 1a5a4f734dc946f5d89d617ef156ccb571056036..8c9171ccf45cedbb24fe1cb3890948f17093f190 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -25,6 +25,7 @@
  */
 
 #include "linden_common.h"
+#include "llmemory.h"
 
 #include <boost/static_assert.hpp>
 #include "llsys.h"
@@ -33,6 +34,7 @@
 #include "llglheaders.h"
 #include "llmemtype.h"
 #include "llrender.h"
+#include "llvector4a.h"
 
 //============================================================================
 
@@ -57,20 +59,24 @@ BOOL LLVertexBuffer::sIBOActive = FALSE;
 U32 LLVertexBuffer::sAllocatedBytes = 0;
 BOOL LLVertexBuffer::sMapped = FALSE;
 BOOL LLVertexBuffer::sUseStreamDraw = TRUE;
+BOOL LLVertexBuffer::sPreferStreamDraw = FALSE;
+S32	LLVertexBuffer::sWeight4Loc = -1;
 
 std::vector<U32> LLVertexBuffer::sDeleteList;
 
-S32 LLVertexBuffer::sTypeOffsets[LLVertexBuffer::TYPE_MAX] =
+
+S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
 {
-	sizeof(LLVector3), // TYPE_VERTEX,
-	sizeof(LLVector3), // TYPE_NORMAL,
+	sizeof(LLVector4), // TYPE_VERTEX,
+	sizeof(LLVector4), // TYPE_NORMAL,
 	sizeof(LLVector2), // TYPE_TEXCOORD0,
 	sizeof(LLVector2), // TYPE_TEXCOORD1,
 	sizeof(LLVector2), // TYPE_TEXCOORD2,
 	sizeof(LLVector2), // TYPE_TEXCOORD3,
 	sizeof(LLColor4U), // TYPE_COLOR,
-	sizeof(LLVector3), // TYPE_BINORMAL,
+	sizeof(LLVector4), // TYPE_BINORMAL,
 	sizeof(F32),	   // TYPE_WEIGHT,
+	sizeof(LLVector4), // TYPE_WEIGHT4,
 	sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
 };
 
@@ -139,11 +145,11 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask)
 			}
 			else 
 			{	//was disabled
-				if (data_mask & mask[i])
+				if (data_mask & mask[i] && i > 0)
 				{ //needs to be enabled
 					glEnableClientState(array[i]);
 				}
-				else if (gDebugGL && glIsEnabled(array[i]))
+				else if (gDebugGL && i > 0 && glIsEnabled(array[i]))
 				{ //needs to be disabled, make sure it was (DEBUG TEMPORARY)
 					if (gDebugSession)
 					{
@@ -205,18 +211,53 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask)
 			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
 	
+		if (sLastMask & MAP_WEIGHT4)
+		{
+			if (sWeight4Loc < 0)
+			{
+				llerrs << "Weighting disabled but vertex buffer still bound!" << llendl;
+			}
+
+			if (!(data_mask & MAP_WEIGHT4))
+			{ //disable 4-component skin weight			
+				glDisableVertexAttribArrayARB(sWeight4Loc);
+			}
+		}
+		else if (data_mask & MAP_WEIGHT4)
+		{
+			if (sWeight4Loc >= 0)
+			{ //enable 4-component skin weight
+				glEnableVertexAttribArrayARB(sWeight4Loc);
+			}
+		}
+				
+
 		sLastMask = data_mask;
 	}
 }
 
-void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
+//static
+void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
 {
-	llassert(mRequestedNumVerts >= 0);
+	U32 count = pos.size();
+	llassert(norm.size() >= pos.size());
+
+	unbind();
+	
+	setupClientArrays(MAP_VERTEX | MAP_NORMAL);
+
+	glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
+	glNormalPointer(GL_FLOAT, 0, norm[0].mV);
+
+	glDrawArrays(sGLMode[mode], 0, count);
+}
 
+void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
+{
 	if (start >= (U32) mRequestedNumVerts ||
 	    end >= (U32) mRequestedNumVerts)
 	{
-		llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "]" << llendl;
+		llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mRequestedNumVerts << llendl;
 	}
 
 	llassert(mRequestedNumIndices >= 0);
@@ -227,6 +268,25 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi
 		llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
 	}
 
+	if (gDebugGL && !useVBOs())
+	{
+		U16* idx = ((U16*) getIndicesPointer())+indices_offset;
+		for (U32 i = 0; i < count; ++i)
+		{
+			if (idx[i] < start || idx[i] > end)
+			{
+				llerrs << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << llendl;
+			}
+		}
+	}
+}
+
+void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
+{
+	validateRange(start, end, count, indices_offset);
+
+	llassert(mRequestedNumVerts >= 0);
+
 	if (mGLIndices != sGLRenderIndices)
 	{
 		llerrs << "Wrong index buffer bound." << llendl;
@@ -243,16 +303,17 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi
 		return;
 	}
 
+	U16* idx = ((U16*) getIndicesPointer())+indices_offset;
+
 	stop_glerror();
 	glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT, 
-		((U16*) getIndicesPointer()) + indices_offset);
+		idx);
 	stop_glerror();
 }
 
 void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
 {
 	llassert(mRequestedNumIndices >= 0);
-
 	if (indices_offset >= (U32) mRequestedNumIndices ||
 	    indices_offset + count > (U32) mRequestedNumIndices)
 	{
@@ -284,7 +345,6 @@ void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
 void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
 {
 	llassert(mRequestedNumVerts >= 0);
-
 	if (first >= (U32) mRequestedNumVerts ||
 	    first + count > (U32) mRequestedNumVerts)
 	{
@@ -351,6 +411,8 @@ void LLVertexBuffer::cleanupClass()
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
 	unbind();
 	clientCopy(); // deletes GL buffers
+
+	//llassert_always(!sCount) ;
 }
 
 void LLVertexBuffer::clientCopy(F64 max_time)
@@ -395,22 +457,29 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
 		mUsage = 0;
 	}
 	
-	if (mUsage == GL_STREAM_DRAW_ARB && !sUseStreamDraw)
+	if (mUsage == GL_DYNAMIC_DRAW_ARB && sPreferStreamDraw)
 	{
-		mUsage = 0;
+		mUsage = GL_STREAM_DRAW_ARB;
 	}
 
-	S32 stride = calcStride(typemask, mOffsets);
+	//zero out offsets
+	for (U32 i = 0; i < TYPE_MAX; i++)
+	{
+		mOffsets[i] = 0;
+	}
 
 	mTypeMask = typemask;
-	mStride = stride;
+	mSize = 0;
+	mAlignedOffset = 0;
+	mAlignedIndexOffset = 0;
+
 	sCount++;
 }
 
 //static
-S32 LLVertexBuffer::calcStride(const U32& typemask, S32* offsets)
+S32 LLVertexBuffer::calcOffsets(const U32& typemask, S32* offsets, S32 num_vertices)
 {
-	S32 stride = 0;
+	S32 offset = 0;
 	for (S32 i=0; i<TYPE_MAX; i++)
 	{
 		U32 mask = 1<<i;
@@ -418,13 +487,35 @@ S32 LLVertexBuffer::calcStride(const U32& typemask, S32* offsets)
 		{
 			if (offsets)
 			{
-				offsets[i] = stride;
+				offsets[i] = offset;
+				offset += LLVertexBuffer::sTypeSize[i]*num_vertices;
+				offset = (offset + 0xF) & ~0xF;
 			}
-			stride += sTypeOffsets[i];
 		}
 	}
 
-	return stride;
+	return offset+16;
+}
+
+//static 
+S32 LLVertexBuffer::calcVertexSize(const U32& typemask)
+{
+	S32 size = 0;
+	for (S32 i = 0; i < TYPE_MAX; i++)
+	{
+		U32 mask = 1<<i;
+		if (typemask & mask)
+		{
+			size += LLVertexBuffer::sTypeSize[i];
+		}
+	}
+
+	return size;
+}
+
+S32 LLVertexBuffer::getSize() const
+{
+	return mSize;
 }
 
 // protected, use unref()
@@ -538,8 +629,7 @@ void LLVertexBuffer::createGLBuffer()
 	{
 		static int gl_buffer_idx = 0;
 		mGLBuffer = ++gl_buffer_idx;
-		mMappedData = new U8[size];
-		memset(mMappedData, 0, size);
+		mMappedData = (U8*) ll_aligned_malloc_16(size);
 	}
 }
 
@@ -560,16 +650,20 @@ void LLVertexBuffer::createGLIndices()
 
 	mEmpty = TRUE;
 
+	//pad by 16 bytes for aligned copies
+	size += 16;
+
 	if (useVBOs())
 	{
+		//pad by another 16 bytes for VBO pointer adjustment
+		size += 16;
 		mMappedIndexData = NULL;
 		genIndices();
 		mResized = TRUE;
 	}
 	else
 	{
-		mMappedIndexData = new U8[size];
-		memset(mMappedIndexData, 0, size);
+		mMappedIndexData = (U8*) ll_aligned_malloc_16(size);
 		static int gl_buffer_idx = 0;
 		mGLIndices = ++gl_buffer_idx;
 	}
@@ -592,7 +686,7 @@ void LLVertexBuffer::destroyGLBuffer()
 		}
 		else
 		{
-			delete [] mMappedData;
+			ll_aligned_free_16(mMappedData);
 			mMappedData = NULL;
 			mEmpty = TRUE;
 		}
@@ -601,7 +695,7 @@ void LLVertexBuffer::destroyGLBuffer()
 	}
 	
 	mGLBuffer = 0;
-	unbind();
+	//unbind();
 }
 
 void LLVertexBuffer::destroyGLIndices()
@@ -617,11 +711,11 @@ void LLVertexBuffer::destroyGLIndices()
 			{
 				llerrs << "Vertex buffer destroyed while mapped." << llendl;
 			}
-			releaseIndices();			
+			releaseIndices();
 		}
 		else
 		{
-			delete [] mMappedIndexData;
+			ll_aligned_free_16(mMappedIndexData);
 			mMappedIndexData = NULL;
 			mEmpty = TRUE;
 		}
@@ -630,7 +724,7 @@ void LLVertexBuffer::destroyGLIndices()
 	}
 
 	mGLIndices = 0;
-	unbind();
+	//unbind();
 }
 
 void LLVertexBuffer::updateNumVerts(S32 nverts)
@@ -646,7 +740,7 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
 	}
 
 	mRequestedNumVerts = nverts;
-	
+
 	if (!mDynamicSize)
 	{
 		mNumVerts = nverts;
@@ -661,7 +755,7 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
 		}
 		mNumVerts = nverts;
 	}
-
+	mSize = calcOffsets(mTypeMask, mOffsets, mNumVerts);
 }
 
 void LLVertexBuffer::updateNumIndices(S32 nindices)
@@ -692,6 +786,12 @@ void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER);
 		
+	if (nverts < 0 || nindices < 0 ||
+		nverts > 65536)
+	{
+		llerrs << "Bad vertex buffer allocation: " << nverts << " : " << nindices << llendl;
+	}
+
 	updateNumVerts(nverts);
 	updateNumIndices(nindices);
 	
@@ -730,9 +830,6 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
 	{
 		sAllocatedBytes -= getSize() + getIndicesSize();
 		
-		S32 oldsize = getSize();
-		S32 old_index_size = getIndicesSize();
-
 		updateNumVerts(newnverts);		
 		updateNumIndices(newnindices);
 		
@@ -749,26 +846,10 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
 			}
 			else
 			{
-				//delete old buffer, keep GL buffer for now
 				if (!useVBOs())
 				{
-					U8* old = mMappedData;
-					mMappedData = new U8[newsize];
-					if (old)
-					{	
-						memcpy(mMappedData, old, llmin(newsize, oldsize));
-						if (newsize > oldsize)
-						{
-							memset(mMappedData+oldsize, 0, newsize-oldsize);
-						}
-
-						delete [] old;
-					}
-					else
-					{
-						memset(mMappedData, 0, newsize);
-						mEmpty = TRUE;
-					}
+					ll_aligned_free_16(mMappedData);
+					mMappedData = (U8*) ll_aligned_malloc_16(newsize);
 				}
 				mResized = TRUE;
 			}
@@ -788,24 +869,8 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
 			{
 				if (!useVBOs())
 				{
-					//delete old buffer, keep GL buffer for now
-					U8* old = mMappedIndexData;
-					mMappedIndexData = new U8[new_index_size];
-					
-					if (old)
-					{	
-						memcpy(mMappedIndexData, old, llmin(new_index_size, old_index_size));
-						if (new_index_size > old_index_size)
-						{
-							memset(mMappedIndexData+old_index_size, 0, new_index_size - old_index_size);
-						}
-						delete [] old;
-					}
-					else
-					{
-						memset(mMappedIndexData, 0, new_index_size);
-						mEmpty = TRUE;
-					}
+					ll_aligned_free_16(mMappedIndexData);
+					mMappedIndexData = (U8*) ll_aligned_malloc_16(new_index_size);
 				}
 				mResized = TRUE;
 			}
@@ -846,8 +911,8 @@ void LLVertexBuffer::freeClientBuffer()
 {
 	if(useVBOs() && sDisableVBOMapping && (mMappedData || mMappedIndexData))
 	{
-		delete[] mMappedData ;
-		delete[] mMappedIndexData ;
+		ll_aligned_free_16(mMappedData) ;
+		ll_aligned_free_16(mMappedIndexData) ;
 		mMappedData = NULL ;
 		mMappedIndexData = NULL ;
 	}
@@ -857,9 +922,7 @@ void LLVertexBuffer::allocateClientVertexBuffer()
 {
 	if(!mMappedData)
 	{
-		U32 size = getSize() ;
-		mMappedData = new U8[size];
-		memset(mMappedData, 0, size);
+		mMappedData = (U8*)ll_aligned_malloc_16(getSize());
 	}
 }
 
@@ -867,9 +930,7 @@ void LLVertexBuffer::allocateClientIndexBuffer()
 {
 	if(!mMappedIndexData)
 	{
-		U32 size = getIndicesSize();
-		mMappedIndexData = new U8[size];
-		memset(mMappedIndexData, 0, size);
+		mMappedIndexData = (U8*)ll_aligned_malloc_16(getIndicesSize());
 	}
 }
 
@@ -900,11 +961,15 @@ U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access)
 			}
 			else
 			{
-				mMappedData = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				U8* src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				mMappedData = LL_NEXT_ALIGNED_ADDRESS<U8>(src);
+				mAlignedOffset = mMappedData - src;
+			
+				stop_glerror();
 			}
-			stop_glerror();
 		}
 		
+		
 		if (!mMappedData)
 		{
 			log_glerror();
@@ -916,7 +981,7 @@ U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access)
 			llinfos << "Available virtual memory(KB): " << avail_vir_mem << llendl;
 
 			if(!sDisableVBOMapping)
-			{
+			{			
 				//--------------------
 				//print out more debug info before crash
 				llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ;
@@ -932,7 +997,7 @@ U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access)
 					llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
 				}
 
-				
+							
 				llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl;
 			}
 			else
@@ -973,9 +1038,11 @@ U8* LLVertexBuffer::mapIndexBuffer(S32 access)
 			}
 			else
 			{
-				mMappedIndexData = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				U8* src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				mMappedIndexData = LL_NEXT_ALIGNED_ADDRESS<U8>(src);
+				mAlignedIndexOffset = mMappedIndexData - src;
+				stop_glerror();
 			}
-			stop_glerror();
 		}
 
 		if (!mMappedIndexData)
@@ -1066,7 +1133,6 @@ void LLVertexBuffer::unmapBuffer(S32 type)
 			//throw out client data (we won't be using it again)
 			mEmpty = TRUE;
 			mFinal = TRUE;
-
 			if(sDisableVBOMapping)
 			{
 				freeClientBuffer() ;
@@ -1104,7 +1170,7 @@ template <class T,S32 type> struct VertexBufferStrider
 		}
 		else if (vbo.hasDataType(type))
 		{
-			S32 stride = vbo.getStride();
+			S32 stride = LLVertexBuffer::sTypeSize[type];
 
 			if (vbo.mapVertexBuffer(type) == NULL)
 			{
@@ -1112,7 +1178,7 @@ template <class T,S32 type> struct VertexBufferStrider
 				return FALSE;
 			}
 
-			strider = (T*)(vbo.getMappedData() + vbo.getOffset(type) + index*stride);
+			strider = (T*)(vbo.getMappedData() + vbo.getOffset(type)+index*stride);
 			strider.setStride(stride);
 			return TRUE;
 		}
@@ -1124,7 +1190,6 @@ template <class T,S32 type> struct VertexBufferStrider
 	}
 };
 
-
 bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector3>& strider, S32 index)
 {
 	return VertexBufferStrider<LLVector3,TYPE_VERTEX>::get(*this, strider, index);
@@ -1165,28 +1230,15 @@ bool LLVertexBuffer::getWeightStrider(LLStrider<F32>& strider, S32 index)
 {
 	return VertexBufferStrider<F32,TYPE_WEIGHT>::get(*this, strider, index);
 }
-bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index)
+
+bool LLVertexBuffer::getWeight4Strider(LLStrider<LLVector4>& strider, S32 index)
 {
-	return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector4,TYPE_WEIGHT4>::get(*this, strider, index);
 }
 
-void LLVertexBuffer::setStride(S32 type, S32 new_stride)
+bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index)
 {
-	LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_STRIDE);
-	if (mNumVerts)
-	{
-		llerrs << "LLVertexBuffer::setOffset called with mNumVerts = " << mNumVerts << llendl;
-	}
-	// This code assumes that setStride() will only be called once per VBO per type.
-	S32 delta = new_stride - sTypeOffsets[type];
-	for (S32 i=type+1; i<TYPE_MAX; i++)
-	{
-		if (mTypeMask & (1<<i))
-		{
-			mOffsets[i] += delta;
-		}
-	}
-	mStride += delta;
+	return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index);
 }
 
 //----------------------------------------------------------------------------
@@ -1385,8 +1437,7 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER);
 	stop_glerror();
-	U8* base = useVBOs() ? NULL : mMappedData;
-	S32 stride = mStride;
+	U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
 
 	if ((data_mask & mTypeMask) != data_mask)
 	{
@@ -1395,52 +1446,58 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const
 
 	if (data_mask & MAP_NORMAL)
 	{
-		glNormalPointer(GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_NORMAL]));
+		glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL]));
 	}
 	if (data_mask & MAP_TEXCOORD3)
 	{
 		glClientActiveTextureARB(GL_TEXTURE3_ARB);
-		glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD3]));
+		glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD3], (void*)(base + mOffsets[TYPE_TEXCOORD3]));
 		glClientActiveTextureARB(GL_TEXTURE0_ARB);
 	}
 	if (data_mask & MAP_TEXCOORD2)
 	{
 		glClientActiveTextureARB(GL_TEXTURE2_ARB);
-		glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+		glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD2], (void*)(base + mOffsets[TYPE_TEXCOORD2]));
 		glClientActiveTextureARB(GL_TEXTURE0_ARB);
 	}
 	if (data_mask & MAP_TEXCOORD1)
 	{
 		glClientActiveTextureARB(GL_TEXTURE1_ARB);
-		glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD1]));
+		glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1]));
 		glClientActiveTextureARB(GL_TEXTURE0_ARB);
 	}
 	if (data_mask & MAP_BINORMAL)
 	{
 		glClientActiveTextureARB(GL_TEXTURE2_ARB);
-		glTexCoordPointer(3,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_BINORMAL]));
+		glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], (void*)(base + mOffsets[TYPE_BINORMAL]));
 		glClientActiveTextureARB(GL_TEXTURE0_ARB);
 	}
 	if (data_mask & MAP_TEXCOORD0)
 	{
-		glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD0]));
+		glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
 	}
 	if (data_mask & MAP_COLOR)
 	{
-		glColorPointer(4, GL_UNSIGNED_BYTE, stride, (void*)(base + mOffsets[TYPE_COLOR]));
+		glColorPointer(4, GL_UNSIGNED_BYTE, LLVertexBuffer::sTypeSize[TYPE_COLOR], (void*)(base + mOffsets[TYPE_COLOR]));
 	}
 	
 	if (data_mask & MAP_WEIGHT)
 	{
-		glVertexAttribPointerARB(1, 1, GL_FLOAT, FALSE, stride, (void*)(base + mOffsets[TYPE_WEIGHT]));
+		glVertexAttribPointerARB(1, 1, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT], (void*)(base + mOffsets[TYPE_WEIGHT]));
+	}
+
+	if (data_mask & MAP_WEIGHT4 && sWeight4Loc != -1)
+	{
+		glVertexAttribPointerARB(sWeight4Loc, 4, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], (void*)(base+mOffsets[TYPE_WEIGHT4]));
 	}
+
 	if (data_mask & MAP_CLOTHWEIGHT)
 	{
-		glVertexAttribPointerARB(4, 4, GL_FLOAT, TRUE,  stride, (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
+		glVertexAttribPointerARB(4, 4, GL_FLOAT, TRUE,  LLVertexBuffer::sTypeSize[TYPE_CLOTHWEIGHT], (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
 	}
 	if (data_mask & MAP_VERTEX)
 	{
-		glVertexPointer(3,GL_FLOAT, stride, (void*)(base + 0));
+		glVertexPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], (void*)(base + 0));
 	}
 
 	llglassertok();
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index c51ce7ac4e1119bd8b4d14c583f94ea2c032a37c..a9f22193f87a4601aa25b94da85ad4fdb7ded85b 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -56,42 +56,65 @@ class LLVBOPool : public LLGLNamePool
 	virtual GLuint allocateName()
 	{
 		GLuint name;
+		stop_glerror();
 		glGenBuffersARB(1, &name);
+		stop_glerror();
 		return name;
 	}
 
 	virtual void releaseName(GLuint name)
 	{
+		stop_glerror();
 		glDeleteBuffersARB(1, &name);
+		stop_glerror();
 	}
 };
 
 
 //============================================================================
-// base class
+// base class 
 
 class LLVertexBuffer : public LLRefCount
 {
 public:
+	LLVertexBuffer(const LLVertexBuffer& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLVertexBuffer& operator=(const LLVertexBuffer& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
 	static LLVBOPool sStreamVBOPool;
 	static LLVBOPool sDynamicVBOPool;
 	static LLVBOPool sStreamIBOPool;
 	static LLVBOPool sDynamicIBOPool;
 
+	static S32	sWeight4Loc;
+
 	static BOOL	sUseStreamDraw;
+	static BOOL	sPreferStreamDraw;
 
 	static void initClass(bool use_vbo, bool no_vbo_mapping);
 	static void cleanupClass();
 	static void setupClientArrays(U32 data_mask);
+	static void drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm);
+
  	static void clientCopy(F64 max_time = 0.005); //copy data from client to GL
 	static void unbind(); //unbind any bound vertex buffer
 
 	//get the size of a vertex with the given typemask
-	//if offsets is not NULL, its contents will be filled
-	//with the offset of each vertex component in the buffer, 
+	static S32 calcVertexSize(const U32& typemask);
+
+	//get the size of a buffer with the given typemask and vertex count
+	//fill offsets with the offset of each vertex component array into the buffer
 	// indexed by the following enum
-	static S32 calcStride(const U32& typemask, S32* offsets = NULL); 										
+	static S32 calcOffsets(const U32& typemask, S32* offsets, S32 num_vertices);		
 
+	
 	enum {
 		TYPE_VERTEX,
 		TYPE_NORMAL,
@@ -103,6 +126,7 @@ class LLVertexBuffer : public LLRefCount
 		// These use VertexAttribPointer and should possibly be made generic
 		TYPE_BINORMAL,
 		TYPE_WEIGHT,
+		TYPE_WEIGHT4,
 		TYPE_CLOTHWEIGHT,
 		TYPE_MAX,
 		TYPE_INDEX,
@@ -118,6 +142,7 @@ class LLVertexBuffer : public LLRefCount
 		// These use VertexAttribPointer and should possibly be made generic
 		MAP_BINORMAL = (1<<TYPE_BINORMAL),
 		MAP_WEIGHT = (1<<TYPE_WEIGHT),
+		MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
 		MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
 	};
 	
@@ -172,6 +197,7 @@ class LLVertexBuffer : public LLRefCount
 	bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0);
 	bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0);
 	bool getWeightStrider(LLStrider<F32>& strider, S32 index=0);
+	bool getWeight4Strider(LLStrider<LLVector4>& strider, S32 index=0);
 	bool getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index=0);
 	
 	BOOL isEmpty() const					{ return mEmpty; }
@@ -181,33 +207,37 @@ class LLVertexBuffer : public LLRefCount
 	S32 getRequestedVerts() const			{ return mRequestedNumVerts; }
 	S32 getRequestedIndices() const			{ return mRequestedNumIndices; }
 
-	U8* getIndicesPointer() const			{ return useVBOs() ? NULL : mMappedIndexData; }
-	U8* getVerticesPointer() const			{ return useVBOs() ? NULL : mMappedData; }
-	S32 getStride() const					{ return mStride; }
-	S32 getTypeMask() const					{ return mTypeMask; }
-	BOOL hasDataType(S32 type) const		{ return ((1 << type) & getTypeMask()) ? TRUE : FALSE; }
-	S32 getSize() const						{ return mNumVerts*mStride; }
+	U8* getIndicesPointer() const			{ return useVBOs() ? (U8*) mAlignedIndexOffset : mMappedIndexData; }
+	U8* getVerticesPointer() const			{ return useVBOs() ? (U8*) mAlignedOffset : mMappedData; }
+	U32 getTypeMask() const					{ return mTypeMask; }
+	bool hasDataType(S32 type) const		{ return ((1 << type) & getTypeMask()); }
+	S32 getSize() const;
 	S32 getIndicesSize() const				{ return mNumIndices * sizeof(U16); }
 	U8* getMappedData() const				{ return mMappedData; }
 	U8* getMappedIndices() const			{ return mMappedIndexData; }
 	S32 getOffset(S32 type) const			{ return mOffsets[type]; }
 	S32 getUsage() const					{ return mUsage; }
 
-	void setStride(S32 type, S32 new_stride);
-	
 	void markDirty(U32 vert_index, U32 vert_count, U32 indices_index, U32 indices_count);
 
 	void draw(U32 mode, U32 count, U32 indices_offset) const;
 	void drawArrays(U32 mode, U32 offset, U32 count) const;
 	void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;
 
+	//for debugging, validate data in given range is valid
+	void validateRange(U32 start, U32 end, U32 count, U32 offset) const;
+
+	
+
 protected:	
 	S32		mNumVerts;		// Number of vertices allocated
 	S32		mNumIndices;	// Number of indices allocated
 	S32		mRequestedNumVerts;  // Number of vertices requested
 	S32		mRequestedNumIndices;  // Number of indices requested
 
-	S32		mStride;
+	ptrdiff_t mAlignedOffset;
+	ptrdiff_t mAlignedIndexOffset;
+	S32		mSize;
 	U32		mTypeMask;
 	S32		mUsage;			// GL usage
 	U32		mGLBuffer;		// GL VBO handle
@@ -248,12 +278,12 @@ class LLVertexBuffer : public LLRefCount
 		
 	static BOOL sDisableVBOMapping; //disable glMapBufferARB
 	static BOOL sEnableVBOs;
-	static BOOL sVBOActive;
-	static BOOL sIBOActive;
-	static S32 sTypeOffsets[TYPE_MAX];
+	static S32 sTypeSize[TYPE_MAX];
 	static U32 sGLMode[LLRender::NUM_MODES];
 	static U32 sGLRenderBuffer;
-	static U32 sGLRenderIndices;	
+	static U32 sGLRenderIndices;
+	static BOOL sVBOActive;
+	static BOOL sIBOActive;
 	static U32 sLastMask;
 	static U32 sAllocatedBytes;
 	static U32 sBindCount;
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 33ab2e93b5cf8c7b0f4c3fc692421395880f736e..684e393cba1c7ae9eb6adcb62dd2c0f2b988d0c7 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -244,12 +244,12 @@ target_link_libraries(llui
     ${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
     )
 
-if(LL_TESTS)
-  # Add tests
-  include(LLAddBuildTest)
-  SET(llui_TEST_SOURCE_FILES
-      llurlmatch.cpp
-      llurlentry.cpp
-      )
-  LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}")
-endif(LL_TESTS)
+# Add tests
+if (LL_TESTS)
+	include(LLAddBuildTest)
+	SET(llui_TEST_SOURCE_FILES
+		llurlmatch.cpp
+		llurlentry.cpp
+		)
+	LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}")
+endif (LL_TESTS)
\ No newline at end of file
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 6f9893b07afcb0d2bb7e5b2a8a25d6f4ea1b2678..a4d1854bc81d7bf957ba0cf072f1ae87264169f2 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -231,6 +231,10 @@ void	LLComboBox::resetDirty()
 	}
 }
 
+bool LLComboBox::itemExists(const std::string& name)
+{
+	return mList->getItemByLabel(name);
+}
 
 // add item "name" to menu
 LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, BOOL enabled)
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
index e9ef9d07e419353c73dab1161cd4db964587daff..64dbaea30634d1d6cb95f88a397de45b907b3187 100644
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -134,6 +134,7 @@ class LLComboBox
 	LLScrollListItem*	addSeparator(EAddPosition pos = ADD_BOTTOM);
 	BOOL			remove( S32 index );	// remove item by index, return TRUE if found and removed
 	void			removeall() { clearRows(); }
+	bool			itemExists(const std::string& name);
 
 	void			sortByName(BOOL ascending = TRUE); // Sort the entries in the combobox by name
 
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
index c57c02f4b12e21d6a73e6e8c1b87c9dc00bcb179..97a52fada4a01c4162d3238478580b14452799ec 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -87,9 +87,6 @@ bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*
 		mItemsPanel->addChild(item);
 		break;
 	default:
-		LL_WARNS("") << "Unsupported position." << LL_ENDL;
-		delete new_pair;
-		return false;
 		break;
 	}
 	
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index 051585369850a330d71a98e031fdc95cfdd62423..92bf429031d216a9a3e85209bb8058816cbe1713 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -450,8 +450,9 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
  */
 class LLFlatListViewEx : public LLFlatListView
 {
-	LOG_CLASS(LLFlatListViewEx);
 public:
+	LOG_CLASS(LLFlatListViewEx);
+
 	struct Params : public LLInitParam::Block<Params, LLFlatListView::Params>
 	{
 		/**
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index d99ee5a545704d93fe43807c623281250facce50..0196080d900a1db729b37dfc75498c16838d39df 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -389,7 +389,11 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
 	setCursor(llmin((S32)mText.length(), getCursor()));
 
 	// Set current history line to end of history.
-	if(mLineHistory.end() != mLineHistory.begin())
+	if (mLineHistory.empty())
+	{
+		mCurrentHistoryLine = mLineHistory.end();
+	}
+	else
 	{
 		mCurrentHistoryLine = mLineHistory.end() - 1;
 	}
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index fd7bb699f851502b01349fed08826e696843b7e5..1cc3cc04d61ca5f2449b24358109049baf4fd3f3 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1164,7 +1164,7 @@ void LLTextBase::reflow()
 	S32 first_line = getFirstVisibleLine();
 
 	// if scroll anchor not on first line, update it to first character of first line
-	if (!mLineInfoList.empty()
+	if ((first_line < mLineInfoList.size())
 		&&	(mScrollIndex <  mLineInfoList[first_line].mDocIndexStart
 			||	mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
 	{
@@ -1658,6 +1658,9 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
 		while ( LLUrlRegistry::instance().findUrl(text, match,
 		        boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) )
 		{
+			
+			LLTextUtil::processUrlMatch(&match,this);
+
 			start = match.getStart();
 			end = match.getEnd()+1;
 
@@ -1679,10 +1682,6 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
 				std::string subtext=text.substr(0,start);
 				appendAndHighlightText(subtext, part, style_params); 
 			}
-
-			// inserts an avatar icon preceding the Url if appropriate
-			LLTextUtil::processUrlMatch(&match,this);
-
 			// output the styled Url
 			appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly());
 			
@@ -2964,11 +2963,18 @@ bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width
 S32	 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
 {
 	LLUIImagePtr image = mStyle->getImage();
+	
+	if (image.isNull())
+	{
+		return 1;
+	}
+
 	S32 image_width = image->getWidth();
 	if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD)
 	{
 		return 1;
 	}
+
 	return 0;
 }
 
@@ -2978,18 +2984,21 @@ F32	LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 select
 	{
 		LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
 		LLUIImagePtr image = mStyle->getImage();
-		S32 style_image_height = image->getHeight();
-		S32 style_image_width = image->getWidth();
-		// Text is drawn from the top of the draw_rect downward
-		
-		S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
-		// Align image to center of draw rect
-		S32 image_bottom = text_center - (style_image_height / 2);
-		image->draw(draw_rect.mLeft, image_bottom, 
-			style_image_width, style_image_height, color);
-		
-		const S32 IMAGE_HPAD = 3;
-		return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+		if (image.notNull())
+		{
+			S32 style_image_height = image->getHeight();
+			S32 style_image_width = image->getWidth();
+			// Text is drawn from the top of the draw_rect downward
+			
+			S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
+			// Align image to center of draw rect
+			S32 image_bottom = text_center - (style_image_height / 2);
+			image->draw(draw_rect.mLeft, image_bottom, 
+				style_image_width, style_image_height, color);
+			
+			const S32 IMAGE_HPAD = 3;
+			return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+		}
 	}
 	return 0.0;
 }
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index d73e87129e3eeabf728bd5ef0a3740d6b6359553..245126d1780c1e3b71369372746bc42e181c72e8 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -114,7 +114,8 @@ LLView::Params::Params()
 }
 
 LLView::LLView(const LLView::Params& p)
-:	mName(p.name),
+:	mVisible(p.visible),
+	mName(p.name),
 	mParentView(NULL),
 	mReshapeFlags(FOLLOWS_NONE),
 	mFromXUI(p.from_xui),
@@ -123,7 +124,6 @@ LLView::LLView(const LLView::Params& p)
 	mNextInsertionOrdinal(0),
 	mHoverCursor(getCursorFromString(p.hover_cursor)),
 	mEnabled(p.enabled),
-	mVisible(p.visible),
 	mMouseOpaque(p.mouse_opaque),
 	mSoundFlags(p.sound_flags),
 	mUseBoundingRect(p.use_bounding_rect),
@@ -1299,7 +1299,13 @@ void LLView::drawChildren()
 {
 	if (!mChildList.empty())
 	{
-		LLRect rootRect = getRootView()->getRect();
+		static const LLRect* rootRect = NULL;
+		
+		if (!mParentView)
+		{
+			rootRect = &mRect;
+		}
+
 		LLRect screenRect;
 
 		++sDepth;
@@ -1313,7 +1319,7 @@ void LLView::drawChildren()
 			{
 				// Only draw views that are within the root view
 				localRectToScreen(viewp->getRect(),&screenRect);
-				if ( rootRect.overlaps(screenRect)  && LLUI::sDirtyRect.overlaps(screenRect))
+				if ( rootRect->overlaps(screenRect)  && LLUI::sDirtyRect.overlaps(screenRect))
 				{
 					LLUI::pushMatrix();
 					{
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 8f167959b9ee370e94d68cb96ab420324c9c8dca..594a5eec6be9b8927f45b76d66c9730ad326dae7 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -288,7 +288,7 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	void 	setAllChildrenEnabled(BOOL b);
 
 	virtual void	setVisible(BOOL visible);
-	BOOL			getVisible() const			{ return mVisible; }
+	const BOOL&		getVisible() const			{ return mVisible; }
 	virtual void	setEnabled(BOOL enabled);
 	BOOL			getEnabled() const			{ return mEnabled; }
 	/// 'available' in this context means 'visible and enabled': in other
@@ -544,11 +544,13 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	LLView*		mParentView;
 	child_list_t mChildList;
 
-	std::string	mName;
 	// location in pixels, relative to surrounding structure, bottom,left=0,0
+	BOOL		mVisible;
 	LLRect		mRect;
 	LLRect		mBoundingRect;
+	
 	std::string mLayout;
+	std::string	mName;
 	
 	U32			mReshapeFlags;
 
@@ -570,8 +572,6 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	LLRootHandle<LLView> mHandle;
 	BOOL		mLastVisible;
 
-	BOOL		mVisible;
-
 	S32			mNextInsertionOrdinal;
 
 	static LLWindow* sWindow;	// All root views must know about their window.
diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt
index a3782d824b4cbbcf0abe31ecb5d754238cf3c8a5..b6d1ce61e5ff5d15aeec971205a78611796f91d7 100644
--- a/indra/llvfs/CMakeLists.txt
+++ b/indra/llvfs/CMakeLists.txt
@@ -74,17 +74,17 @@ if (DARWIN)
 endif (DARWIN)
 
 
-if(LL_TESTS)
-  # Add tests
-  include(LLAddBuildTest)
-  # UNIT TESTS
-  SET(llvfs_TEST_SOURCE_FILES
-      # none so far
-      )
-  LL_ADD_PROJECT_UNIT_TESTS(llvfs "${llvfs_TEST_SOURCE_FILES}")
-
-  # INTEGRATION TESTS
-  set(test_libs llmath llcommon llvfs ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
-  # TODO: Some of these need refactoring to be proper Unit tests rather than Integration tests.
-  LL_ADD_INTEGRATION_TEST(lldir "" "${test_libs}")
-endif(LL_TESTS)
+# Add tests
+if (LL_TESTS)
+	include(LLAddBuildTest)
+	# UNIT TESTS
+	SET(llvfs_TEST_SOURCE_FILES
+	  # none so far
+	  )
+	LL_ADD_PROJECT_UNIT_TESTS(llvfs "${llvfs_TEST_SOURCE_FILES}")
+
+	# INTEGRATION TESTS
+	set(test_libs llmath llcommon llvfs ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+	# TODO: Some of these need refactoring to be proper Unit tests rather than Integration tests.
+	LL_ADD_INTEGRATION_TEST(lldir "" "${test_libs}")
+endif (LL_TESTS)
diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp
index a8db7b235e184b9f4163abe22a23b6c0b16767c8..ca749c5eafb8dbb57fe1d94d490fe4137d898007 100644
--- a/indra/llvfs/llvfile.cpp
+++ b/indra/llvfs/llvfile.cpp
@@ -314,7 +314,7 @@ BOOL LLVFile::setMaxSize(S32 size)
 
 	if (!mVFS->checkAvailable(size))
 	{
-		LLFastTimer t(FTM_VFILE_WAIT);
+		//LLFastTimer t(FTM_VFILE_WAIT);
 		S32 count = 0;
 		while (sVFSThread->getPending() > 1000)
 		{
@@ -422,7 +422,7 @@ bool LLVFile::isLocked(EVFSLock lock)
 
 void LLVFile::waitForLock(EVFSLock lock)
 {
-	LLFastTimer t(FTM_VFILE_WAIT);
+	//LLFastTimer t(FTM_VFILE_WAIT);
 	// spin until the lock clears
 	while (isLocked(lock))
 	{
diff --git a/indra/llvfs/llvfs.cpp b/indra/llvfs/llvfs.cpp
index 78b67e9b68697c1d69034b36beb81cb776cb8746..82c926620a07960695337df7148752bdb9aa5f0a 100644
--- a/indra/llvfs/llvfs.cpp
+++ b/indra/llvfs/llvfs.cpp
@@ -2036,6 +2036,9 @@ std::string get_extension(LLAssetType::EType type)
 	case LLAssetType::AT_ANIMATION:
 		extension = ".lla";
 		break;
+	case LLAssetType::AT_MESH:
+		extension = ".slm";
+		break;
 	default:
 		// Just use the asset server filename extension in most cases
 		extension += ".";
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 447e3661db90709164d7dc10a83b00fbca75b946..cb2abc5bc0442a53b594f862fa90ac62f5271edb 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -536,20 +536,20 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
 				GLint fullscreenAttrib[] =
 				{
 					AGL_RGBA,
-						AGL_FULLSCREEN,
-						//			AGL_NO_RECOVERY,	// MBW -- XXX -- Not sure if we want this attribute
-						AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
-						AGL_SAMPLES_ARB, mFSAASamples,
-						AGL_DOUBLEBUFFER,
-						AGL_CLOSEST_POLICY,
-						AGL_ACCELERATED,
-						AGL_RED_SIZE, 8,
-						AGL_GREEN_SIZE, 8,
-						AGL_BLUE_SIZE, 8,
-						AGL_ALPHA_SIZE, 8,
-						AGL_DEPTH_SIZE, 24,
-						AGL_STENCIL_SIZE, 8,
-						AGL_NONE
+					AGL_FULLSCREEN,
+					AGL_NO_RECOVERY,
+					AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
+					AGL_SAMPLES_ARB, mFSAASamples,
+					AGL_DOUBLEBUFFER,
+					AGL_CLOSEST_POLICY,
+					AGL_ACCELERATED,
+					AGL_RED_SIZE, 8,
+					AGL_GREEN_SIZE, 8,
+					AGL_BLUE_SIZE, 8,
+					AGL_ALPHA_SIZE, 8,
+					AGL_DEPTH_SIZE, 24,
+					AGL_STENCIL_SIZE, 8,
+					AGL_NONE
 				};
 
 				LL_DEBUGS("Window") << "createContext: creating fullscreen pixelformat" << LL_ENDL;
@@ -562,21 +562,28 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
 			}
 			else
 			{
+				// NOTE from Leslie:
+				//
+				// AGL_NO_RECOVERY, when combined with AGL_ACCELERATED prevents software rendering
+				// fallback which means we won't hvae shaders that compile and link but then don't
+				// work.  The drawback is that our shader compilation will be a bit more finicky though.
+
 				GLint windowedAttrib[] =
 				{
 					AGL_RGBA,
-						AGL_DOUBLEBUFFER,
-						AGL_CLOSEST_POLICY,
-						AGL_ACCELERATED,
-						AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
-						AGL_SAMPLES_ARB, mFSAASamples,
-						AGL_RED_SIZE, 8,
-						AGL_GREEN_SIZE, 8,
-						AGL_BLUE_SIZE, 8,
-						AGL_ALPHA_SIZE, 8,
-						AGL_DEPTH_SIZE, 24,
-						AGL_STENCIL_SIZE, 8,
-						AGL_NONE
+					AGL_NO_RECOVERY,
+					AGL_DOUBLEBUFFER,
+					AGL_CLOSEST_POLICY,
+					AGL_ACCELERATED,
+					AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
+					AGL_SAMPLES_ARB, mFSAASamples,
+					AGL_RED_SIZE, 8,
+					AGL_GREEN_SIZE, 8,
+					AGL_BLUE_SIZE, 8,
+					AGL_ALPHA_SIZE, 8,
+					AGL_DEPTH_SIZE, 24,
+					AGL_STENCIL_SIZE, 8,
+					AGL_NONE
 				};
 
 				LL_DEBUGS("Window") << "createContext: creating windowed pixelformat" << LL_ENDL;
diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt
index eb5166ee71f057d0d03e65e283a37fcdb3e3c849..21cdf5f92611cdf1791409dd926d73707f880ba4 100644
--- a/indra/llxml/CMakeLists.txt
+++ b/indra/llxml/CMakeLists.txt
@@ -45,27 +45,25 @@ target_link_libraries( llxml
     ${EXPAT_LIBRARIES}
     )
 
+# tests
 
-if(LL_TESTS)
-  # tests
+if (LL_TESTS)
+	# unit tests
 
-  # unit tests
+	SET(llxml_TEST_SOURCE_FILES
+	  # none yet!
+	  )
+	LL_ADD_PROJECT_UNIT_TESTS(llxml "${llxml_TEST_SOURCE_FILES}")
 
-  SET(llxml_TEST_SOURCE_FILES
-      # none yet!
-      )
-  LL_ADD_PROJECT_UNIT_TESTS(llxml "${llxml_TEST_SOURCE_FILES}")
+	# integration tests
 
-  # integration tests
+	#    set(TEST_DEBUG on)
+	set(test_libs
+	  ${LLXML_LIBRARIES}
+	  ${WINDOWS_LIBRARIES}
+	  ${LLMATH_LIBRARIES}
+	  ${LLCOMMON_LIBRARIES}
+	  )
 
-  # set(TEST_DEBUG on)
-  set(test_libs
-      ${LLXML_LIBRARIES}
-      ${WINDOWS_LIBRARIES}
-      ${LLMATH_LIBRARIES}
-      ${LLCOMMON_LIBRARIES}
-      )
-
-  LL_ADD_INTEGRATION_TEST(llcontrol "" "${test_libs}")
-
-endif(LL_TESTS)
+	LL_ADD_INTEGRATION_TEST(llcontrol "" "${test_libs}")
+endif (LL_TESTS)
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 95cfc23edeb43a768622cd70e962d730a545efdf..2ecce0ebd38f5a523631c8ed639064b779a1e726 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -17,6 +17,7 @@ include(JsonCpp)
 include(LLAudio)
 include(LLCharacter)
 include(LLCommon)
+include(LLConvexDecomposition)
 include(LLImage)
 include(LLImageJ2COJ)
 include(LLInventory)
@@ -40,14 +41,17 @@ include(UnixInstall)
 include(LLKDU)
 include(ViewerMiscLibs)
 include(LLLogin)
+include(GLOD)
 include(CMakeCopyIfDifferent)
 
 include_directories(
     ${DBUSGLIB_INCLUDE_DIRS}
     ${JSONCPP_INCLUDE_DIRS}
+    ${GLOD_INCLUDE_DIR}
     ${LLAUDIO_INCLUDE_DIRS}
     ${LLCHARACTER_INCLUDE_DIRS}
     ${LLCOMMON_INCLUDE_DIRS}
+    ${LLCONVEXDECOMP_INCLUDE_DIRS}
     ${FMOD_INCLUDE_DIR}
     ${LLIMAGE_INCLUDE_DIRS}
     ${LLKDU_INCLUDE_DIRS}
@@ -66,7 +70,9 @@ include_directories(
     ${LSCRIPT_INCLUDE_DIRS}/lscript_compile
     ${LLLOGIN_INCLUDE_DIRS}
     ${UPDATER_INCLUDE_DIRS}
+    ${LIBS_PREBUILT_DIR}/include/collada
     ${OPENAL_LIB_INCLUDE_DIRS}
+    ${LIBS_PREBUILT_DIR}/include/collada/1.4
     )
 
 set(viewer_SOURCE_FILES
@@ -195,6 +201,8 @@ set(viewer_SOURCE_FILES
     llfloatermediabrowser.cpp
     llfloatermediasettings.cpp
     llfloatermemleak.cpp
+    llfloatermodelpreview.cpp
+    llfloatermodelwizard.cpp
     llfloaternamedesc.cpp
     llfloaternotificationsconsole.cpp
     llfloateropenobject.cpp
@@ -299,6 +307,7 @@ set(viewer_SOURCE_FILES
     llmediadataclient.cpp
     llmemoryview.cpp
     llmenucommands.cpp
+    llmeshrepository.cpp
     llmimetypes.cpp
     llmorphview.cpp
     llmoveview.cpp
@@ -382,6 +391,7 @@ set(viewer_SOURCE_FILES
     llparticipantlist.cpp
     llpatchvertexarray.cpp
     llphysicsmotion.cpp
+    llphysicsshapebuilderutil.cpp
     llplacesinventorybridge.cpp
     llplacesinventorypanel.cpp
     llpopupview.cpp
@@ -401,6 +411,7 @@ set(viewer_SOURCE_FILES
     llremoteparcelrequest.cpp
     llsavedsettingsglue.cpp
     llsaveoutfitcombobtn.cpp
+    llsceneview.cpp
     llscreenchannel.cpp
     llscriptfloater.cpp
     llscrollingpanelparam.cpp
@@ -740,6 +751,8 @@ set(viewer_HEADER_FILES
     llfloatermediabrowser.h
     llfloatermediasettings.h
     llfloatermemleak.h
+    llfloatermodelpreview.h
+    llfloatermodelwizard.h
     llfloaternamedesc.h
     llfloaternotificationsconsole.h
     llfloateropenobject.h
@@ -844,6 +857,7 @@ set(viewer_HEADER_FILES
     llmediadataclient.h
     llmemoryview.h
     llmenucommands.h
+    llmeshrepository.h
     llmimetypes.h
     llmorphview.h
     llmoveview.h
@@ -921,6 +935,7 @@ set(viewer_HEADER_FILES
     llparticipantlist.h
     llpatchvertexarray.h
     llphysicsmotion.h
+    llphysicsshapebuilderutil.h
     llplacesinventorybridge.h
     llplacesinventorypanel.h
     llpolymesh.h
@@ -942,6 +957,7 @@ set(viewer_HEADER_FILES
     llrootview.h
     llsavedsettingsglue.h
     llsaveoutfitcombobtn.h
+    llsceneview.h
     llscreenchannel.h
     llscriptfloater.h
     llscrollingpanelparam.h
@@ -1441,11 +1457,11 @@ set(PACKAGE ON CACHE BOOL
     "Add a package target that builds an installer package.")
 
 if (WINDOWS)
-    set_target_properties(${VIEWER_BINARY_NAME}
+	set_target_properties(${VIEWER_BINARY_NAME}
         PROPERTIES
         # *TODO -reenable this once we get server usage sorted out
         #LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:\"__tcmalloc\""
-        LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS"
+        LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc"
         LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO"
         LINK_FLAGS_RELEASE ""
         )
@@ -1484,6 +1500,12 @@ if (WINDOWS)
       ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libapr-1.dll
       ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libaprutil-1.dll
       ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libapriconv-1.dll
+      ${SHARED_LIB_STAGING_DIR}/Release/glod.dll
+      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/glod.dll
+      ${SHARED_LIB_STAGING_DIR}/Debug/glod.dll
+      ${SHARED_LIB_STAGING_DIR}/Release/libcollada14dom22.dll
+      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libcollada14dom22.dll
+      ${SHARED_LIB_STAGING_DIR}/Debug/libcollada14dom22-d.dll
       ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll
       ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll
       ${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll
@@ -1656,6 +1678,7 @@ endif (WINDOWS)
 # that they depend upon. -brad
 target_link_libraries(${VIEWER_BINARY_NAME}
     ${UPDATER_LIBRARIES}
+	${GOOGLE_PERFTOOLS_LIBRARIES}
     ${LLAUDIO_LIBRARIES}
     ${LLCHARACTER_LIBRARIES}
     ${LLIMAGE_LIBRARIES}
@@ -1680,6 +1703,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${DBUSGLIB_LIBRARIES}
     ${OPENGL_LIBRARIES}
     ${FMODWRAPPER_LIBRARY} # must come after LLAudio
+    ${GLOD_LIBRARIES}
     ${OPENGL_LIBRARIES}
     ${JSONCPP_LIBRARIES}
     ${SDL_LIBRARY}
@@ -1691,7 +1715,8 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${OPENSSL_LIBRARIES}
     ${CRYPTO_LIBRARIES}
     ${LLLOGIN_LIBRARIES}
-    ${GOOGLE_PERFTOOLS_LIBRARIES}
+    ${LLCONVEXDECOMP_LIBRARY}
+    ${TCMALLOC_LIBRARIES}
     )
 
 if (USE_KDU)
@@ -1747,6 +1772,8 @@ if (LINUX)
         ${COPY_INPUT_DEPENDENCIES}
       )
 
+  if (PACKAGE)
+  endif (PACKAGE)
   add_custom_command(
     OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.copy_touched
     COMMAND ${PYTHON_EXECUTABLE}
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
index 4bf67b1367be7b90ee80a892dc5f5bf204d5eb8b..5c7cacedec00fd61740b972f0c2095deddaf3ece 100644
--- a/indra/newview/English.lproj/InfoPlist.strings
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -2,6 +2,6 @@
 
 CFBundleName = "Second Life";
 
-CFBundleShortVersionString = "Second Life version 2.1.1.0";
-CFBundleGetInfoString = "Second Life version 2.1.1.0, Copyright 2004-2010 Linden Research, Inc.";
+CFBundleShortVersionString = "Second Life version 2.1.0.13828";
+CFBundleGetInfoString = "Second Life version 2.1.0.13828, Copyright 2004-2009 Linden Research, Inc.";
 
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 3cda7467dd0e51591e9a69a4dfd0493eeb5b66db..f7b11b217c928cd41a89fa6826256158f9972e34 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -60,7 +60,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>2.1.1.0</string>
+	<string>2.1.0.13828</string>
 	<key>CSResourcesFileMapped</key>
 	<true/>
 </dict>
diff --git a/indra/newview/app_settings/high_graphics.xml b/indra/newview/app_settings/high_graphics.xml
index 4e137d971a6635f6ceafab8437569850bee3e1ef..5bc2e1b7e64f2cbed20033ab50f3d7fc6eea426a 100644
--- a/indra/newview/app_settings/high_graphics.xml
+++ b/indra/newview/app_settings/high_graphics.xml
@@ -4,15 +4,15 @@
 	<RenderAvatarCloth value="FALSE"/>
 	<!--Default for now-->
 	<RenderAvatarLODFactor value="1.0"/>
-  <!--Default for now-->
-  <RenderAvatarPhysicsLODFactor value="0.9"/>
+	<!--Default for now-->
+	<RenderAvatarPhysicsLODFactor value="0.9"/>
 	<!--NO SHADERS-->
 	<RenderAvatarVP value="TRUE"/>
 	<!--Short Range-->
 	<RenderFarClip value="128"/>
 	<!--Default for now-->
 	<RenderFlexTimeFactor value="1"/>
-	<!--256... but they don't use this-->
+	<!--256... but they do not use this-->
 	<RenderGlowResolutionPow value="9"/>
 	<!--Low number-->
 	<RenderMaxPartCount value="4096"/>
@@ -26,8 +26,6 @@
 	<RenderTerrainLODFactor value="2"/>
 	<!--Default for now-->
 	<RenderTreeLODFactor value="0.5"/>
-	<!--Default for now-->
-	<RenderUseFBO value="1"/>
 	<!--Try Impostors-->
 	<RenderUseImpostors value="TRUE"/>
 	<!--Default for now-->
@@ -36,11 +34,10 @@
 	<VertexShaderEnable value="TRUE"/>
 	<!--NO SHADERS-->
 	<WindLightUseAtmosShaders value="TRUE"/>
-  <!--Deferred Shading-->
-  <RenderDeferred value="FALSE"/>
-  <!--SSAO Disabled-->
-  <RenderDeferredSSAO value="FALSE"/>
-  <!--Sun Shadows-->
-  <RenderShadowDetail value="0"/>
-
+	<!--Deferred Shading-->
+	<RenderDeferred value="FALSE"/>
+	<!--SSAO Disabled-->
+	<RenderDeferredSSAO value="FALSE"/>
+	<!--Sun Shadows-->
+	<RenderShadowDetail value="0"/>
 </settings>
diff --git a/indra/newview/app_settings/low_graphics.xml b/indra/newview/app_settings/low_graphics.xml
index 79463b475c4ca010a5ed25499d1cc8a0586aea3d..ca1dae0b86d4a97030806a6deebbafbc3877bd70 100644
--- a/indra/newview/app_settings/low_graphics.xml
+++ b/indra/newview/app_settings/low_graphics.xml
@@ -4,17 +4,17 @@
 	<RenderAvatarCloth value="FALSE"/>
 	<!--Default for now-->
 	<RenderAvatarLODFactor value="0.5"/>
-  <!--Default for now-->
-  <RenderAvatarPhysicsLODFactor value="0.0"/>
-  <!--Default for now-->
-  <RenderAvatarMaxVisible value="3"/>
+	<!--Default for now-->
+	<RenderAvatarPhysicsLODFactor value="0.0"/>
+	<!--Default for now-->
+	<RenderAvatarMaxVisible value="3"/>
 	<!--NO SHADERS-->
 	<RenderAvatarVP value="FALSE"/>
 	<!--Short Range-->
 	<RenderFarClip value="64"/>
 	<!--Default for now-->
 	<RenderFlexTimeFactor value="0.5"/>
-	<!--256... but they don't use this-->
+	<!--256... but they do not use this-->
 	<RenderGlowResolutionPow value="8"/>
 	<!--Low number-->
 	<RenderMaxPartCount value="1024"/>
@@ -28,8 +28,6 @@
 	<RenderTerrainLODFactor value="1.0"/>
 	<!--Default for now-->
 	<RenderTreeLODFactor value="0.5"/>
-	<!--Default for now-->
-	<RenderUseFBO value="0"/>
 	<!--Try Impostors-->
 	<RenderUseImpostors value="TRUE"/>
 	<!--Default for now-->
@@ -38,11 +36,10 @@
 	<VertexShaderEnable value="FALSE"/>
 	<!--NO SHADERS-->
 	<WindLightUseAtmosShaders value="FALSE"/>
-  <!--No Deferred Shading-->
-  <RenderDeferred value="FALSE"/>
-  <!--SSAO Disabled-->
-  <RenderDeferredSSAO value="FALSE"/>
-  <!--No Shadows-->
-  <RenderShadowDetail value="0"/>
-
+	<!--No Deferred Shading-->
+	<RenderDeferred value="FALSE"/>
+	<!--SSAO Disabled-->
+	<RenderDeferredSSAO value="FALSE"/>
+	<!--No Shadows-->
+	<RenderShadowDetail value="0"/>
 </settings>
diff --git a/indra/newview/app_settings/mid_graphics.xml b/indra/newview/app_settings/mid_graphics.xml
index ab1e2a2e1ca2fecba46d70ca766eaa26c787c152..01822fe64c9828db0af9e1b2517a0471dc0337e6 100644
--- a/indra/newview/app_settings/mid_graphics.xml
+++ b/indra/newview/app_settings/mid_graphics.xml
@@ -4,15 +4,15 @@
 	<RenderAvatarCloth value="FALSE"/>
 	<!--Default for now-->
 	<RenderAvatarLODFactor value="0.5"/>
-  <!--Default for now-->
-  <RenderAvatarPhysicsLODFactor value="0.75"/>
+	<!--Default for now-->
+	<RenderAvatarPhysicsLODFactor value="0.75"/>
 	<!--NO SHADERS-->
 	<RenderAvatarVP value="TRUE"/>
 	<!--Short Range-->
 	<RenderFarClip value="96"/>
 	<!--Default for now-->
 	<RenderFlexTimeFactor value="1"/>
-	<!--256... but they don't use this-->
+	<!--256... but they do not use this-->
 	<RenderGlowResolutionPow value="8"/>
 	<!--Low number-->
 	<RenderMaxPartCount value="2048"/>
@@ -26,8 +26,6 @@
 	<RenderTerrainLODFactor value="1.0"/>
 	<!--Default for now-->
 	<RenderTreeLODFactor value="0.5"/>
-	<!--Default for now-->
-	<RenderUseFBO value="0"/>
 	<!--Try Impostors-->
 	<RenderUseImpostors value="TRUE"/>
 	<!--Default for now-->
@@ -36,11 +34,10 @@
 	<VertexShaderEnable value="TRUE"/>
 	<!--NO SHADERS-->
 	<WindLightUseAtmosShaders value="FALSE"/>
-  <!--No Deferred Shading-->
-  <RenderDeferred value="FALSE"/>
-  <!--SSAO Disabled-->
-  <RenderDeferredSSAO value="FALSE"/>
-  <!--No Shadows-->
-  <RenderShadowDetail value="0"/>
-
+	<!--No Deferred Shading-->
+	<RenderDeferred value="FALSE"/>
+	<!--SSAO Disabled-->
+	<RenderDeferredSSAO value="FALSE"/>
+	<!--No Shadows-->
+	<RenderShadowDetail value="0"/>
 </settings>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 33c5e533be982b87813ac43b5b93f45083560be8..78db307d648b196ceb70d18a6c7d555e1b98f6d9 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1333,7 +1333,68 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
-    <key>CertStore</key>
+
+  <key>CameraFocusTransitionTime</key>
+  <map>
+    <key>Comment</key>
+    <string>How many seconds it takes the camera to transition between focal distances</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>0.5</real>
+  </map>
+
+  <key>CameraFNumber</key>
+  <map>
+    <key>Comment</key>
+    <string>Camera f-number value for DoF effect</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>9.0</real>
+  </map>
+
+  <key>CameraFocalLength</key>
+  <map>
+    <key>Comment</key>
+    <string>Camera focal length for DoF effect (in millimeters)</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>50</real>
+  </map>
+
+  <key>CameraFieldOfView</key>
+  <map>
+    <key>Comment</key>
+    <string>Vertical camera field of view for DoF effect (in degrees)</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>60.0</real>
+  </map>
+
+  <key>CameraAspectRatio</key>
+  <map>
+    <key>Comment</key>
+    <string>Camera aspect ratio for DoF effect</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>1.5</real>
+  </map>
+
+  <key>CertStore</key>
     <map>
       <key>Comment</key>
       <string>Specifies the Certificate Store for certificate trust verification</string>
@@ -1855,7 +1916,7 @@
     <key>DebugShowRenderInfo</key>
     <map>
       <key>Comment</key>
-      <string>Show depth buffer contents</string>
+      <string>Show stats about current scene</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
@@ -1863,6 +1924,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+  <key>DebugShowUploadCost</key>
+  <map>
+    <key>Comment</key>
+    <string>Show what it would cost to upload assets in current scene</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
   <key>DebugShowRenderMatrices</key>
   <map>
     <key>Comment</key>
@@ -5484,7 +5556,40 @@
       <key>Value</key>
       <real>0</real>
     </map>
-    <key>MigrateCacheDirectory</key>
+  <key>MeshEnabled</key>
+  <map>
+    <key>Comment</key>
+    <string>Expose UI for mesh functionality (may require restart to take effect).</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <real>1</real>
+  </map>
+  <key>MeshImportUseSLM</key>
+  <map>
+    <key>Comment</key>
+    <string>Use cached copy of last upload for a dae if available instead of loading dae file from scratch.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <real>0</real>
+  </map>
+  <key>MeshUseWholeModelUpload</key>
+  <map>
+    <key>Comment</key>
+    <string>Upload model in its entirety instead of mesh-by-mesh (new caps)</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <real>0</real>
+  </map>
+  <key>MigrateCacheDirectory</key>
     <map>
       <key>Comment</key>
       <string>Check for old version of disk cache to migrate to current location</string>
@@ -6147,6 +6252,66 @@
       <key>Value</key>
       <real>0.0</real>
     </map>
+  <key>ObjectCostHighThreshold</key>
+  <map>
+    <key>Comment</key>
+    <string>Threshold at which object cost is considered high (displayed in red).</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>50.0</real>
+  </map>
+  <key>ObjectCostLowColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Color for object with a low object cost.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.0</real>
+      <real>0.5</real>
+      <real>1.0</real>
+      <real>0.5</real>
+    </array>
+  </map>
+  <key>ObjectCostMidColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Color for object with a medium object cost.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>0.75</real>
+      <real>0.0</real>
+      <real>0.65</real>
+    </array>
+  </map>
+  <key>ObjectCostHighColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Color for object a high object cost.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>0.0</real>
+      <real>0.0</real>
+      <real>0.75</real>
+    </array>
+  </map>
+  
     <key>ParcelMediaAutoPlayEnable</key>
     <map>
       <key>Comment</key>
@@ -6382,7 +6547,177 @@
       <key>Value</key>
 	  <integer>13</integer>
     </map>
-	<key>PrimMediaMasterEnabled</key>
+
+  <key>PreviewAmbientColor</key>
+  <map>
+    <key>Comment</key>
+    <string>Ambient color of preview render.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.0</real>
+      <real>0.0</real>
+      <real>0.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+
+  <key>PreviewDiffuse0</key>
+  <map>
+    <key>Comment</key>
+    <string>Diffise color of preview light 0.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+  <key>PreviewDiffuse1</key>
+  <map>
+    <key>Comment</key>
+    <string>Diffise color of preview light 1.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>0.25</real>
+      <real>0.25</real>
+      <real>0.25</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+  <key>PreviewDiffuse2</key>
+  <map>
+    <key>Comment</key>
+    <string>Diffise color of preview light 2.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+  <key>PreviewSpecular0</key>
+  <map>
+    <key>Comment</key>
+    <string>Diffise color of preview light 0.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+  <key>PreviewSpecular1</key>
+  <map>
+    <key>Comment</key>
+    <string>Diffise color of preview light 1.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+  <key>PreviewSpecular2</key>
+  <map>
+    <key>Comment</key>
+    <string>Diffise color of preview light 2.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Color4</string>
+    <key>Value</key>
+    <array>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+
+  <key>PreviewDirection0</key>
+  <map>
+    <key>Comment</key>
+    <string>Direction of light 0 for preview render.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Vector3</string>
+    <key>Value</key>
+    <array>
+      <real>-0.75</real>
+      <real>1</real>
+      <real>1.0</real>
+    </array>
+  </map>
+
+  <key>PreviewDirection1</key>
+  <map>
+    <key>Comment</key>
+    <string>Direction of light 1 for preview render.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Vector3</string>
+    <key>Value</key>
+    <array>
+      <real>0.5</real>
+      <real>-0.6</real>
+      <real>0.4</real>
+    </array>
+  </map>
+  
+  <key>PreviewDirection2</key>
+  <map>
+    <key>Comment</key>
+    <string>Direction of light 2 for preview render.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Vector3</string>
+    <key>Value</key>
+    <array>
+      <real>0.5</real>
+      <real>-0.8</real>
+      <real>0.3</real>
+    </array>
+  </map>
+
+  <key>PrimMediaMasterEnabled</key>
 	<map>
 	  <key>Comment</key>
       <string>Whether or not Media on a Prim is enabled.</string>
@@ -6866,7 +7201,31 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
-  
+    <key>RenderPerformanceTest</key>
+    <map>
+      <key>Comment</key>
+      <string>Disable rendering of everything but in-world content for 
+        performance testing</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
+   
+  <key>RenderLocalLights</key>
+  <map>
+    <key>Comment</key>
+    <string>Whether or not to render local lights.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>1</integer>
+  </map>
+
   <key>RenderShadowNearDist</key>
   <map>
     <key>Comment</key>
@@ -7003,7 +7362,7 @@
     <string>Vector3</string>
     <key>Value</key>
     <array>
-      <real>0.40</real>
+      <real>0.80</real>
       <real>1.00</real>
       <real>0.00</real>
     </array>
@@ -7063,7 +7422,18 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
-    <key>RenderDebugPipeline</key>
+  <key>RenderDebugNormalScale</key>
+  <map>
+    <key>Comment</key>
+    <string>Scale of normals in debug display.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>0.03</real>
+  </map>
+  <key>RenderDebugPipeline</key>
     <map>
       <key>Comment</key>
       <string>Enable strict pipeline debugging.</string>
@@ -7096,6 +7466,7 @@
     <key>Value</key>
     <integer>0</integer>
   </map>
+ 
   <key>RenderAnimateRes</key>
   <map>
     <key>Comment</key>
@@ -7107,7 +7478,31 @@
     <key>Value</key>
     <integer>0</integer>
   </map>
-  
+
+  <key>RenderBakeSunlight</key>
+  <map>
+    <key>Comment</key>
+    <string>Bake sunlight into vertex buffers for static objects.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+
+  <key>RenderNoAlpha</key>
+  <map>
+    <key>Comment</key>
+    <string>Disable rendering of alpha objects (render all alpha objects as alpha masks).</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+
   <key>RenderAnimateTrees</key>
   <map>
     <key>Comment</key>
@@ -7264,6 +7659,18 @@
     <real>16.0</real>
    </map>
 
+  <key>RenderMinimumLODTriangleCount</key>
+  <map>
+    <key>Comment</key>
+    <string>Triangle count threshold at which automatic LOD generation stops</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <real>16</real>
+  </map>
+
   <key>RenderEdgeDepthCutoff</key>
   <map>
     <key>Comment</key>
@@ -7354,7 +7761,6 @@
     <key>Value</key>
     <real>0.01</real>
   </map>
-
   <key>RenderShadowBiasError</key>
   <map>
     <key>Comment</key>
@@ -7377,6 +7783,18 @@
     <key>Value</key>
     <real>0</real>
   </map>
+
+  <key>RenderDepthOfField</key>
+  <map>
+    <key>Comment</key>
+    <string>Whether to use depth of field effect when lighting and shadows are enabled</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
   
   <key>RenderSpotLightsInNondeferred</key>
   <map>
@@ -7399,7 +7817,7 @@
     <key>Type</key>
     <string>F32</string>
     <key>Value</key>
-    <real>0.0</real>
+    <real>-0.001</real>
   </map>
   <key>RenderSpotShadowOffset</key>
   <map>
@@ -7621,30 +8039,6 @@
     <integer>1</integer>
   </map>
 
-  <key>RenderDeferredLocalLights</key>
-  <map>
-    <key>Comment</key>
-    <string>Execute local lighting shader in deferred renderer.</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Boolean</string>
-    <key>Value</key>
-    <integer>1</integer>
-  </map>
-
-  <key>RenderDeferredFullscreenLights</key>
-  <map>
-    <key>Comment</key>
-    <string>Execute local lighting shader in deferred renderer.</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Boolean</string>
-    <key>Value</key>
-    <integer>1</integer>
-  </map>
-
   <key>RenderDeferredSunWash</key>
   <map>
     <key>Comment</key>
@@ -7687,7 +8081,7 @@
     <key>Type</key>
     <string>F32</string>
     <key>Value</key>
-    <real>1.1</real>
+    <real>0.8</real>
   </map>
 
   <key>RenderShadowGaussian</key>
@@ -7737,7 +8131,7 @@
     <key>Type</key>
     <string>F32</string>
     <key>Value</key>
-    <real>0.1</real>
+    <real>0</real>
   </map>
 
   <key>RenderGIAmbiance</key>
@@ -7957,9 +8351,9 @@
       <string>Vector3</string>
       <key>Value</key>
       <array>
-        <real>0.299</real>
-        <real>0.587</real>
-        <real>0.114</real>
+        <real>1</real>
+        <real>0</real>
+        <real>0</real>
       </array>
     </map>
     <key>RenderGlowMaxExtractAlpha</key>
@@ -7971,7 +8365,7 @@
       <key>Type</key>
       <string>F32</string>
       <key>Value</key>
-      <real>0.065</real>
+      <real>0.25</real>
     </map>
     <key>RenderGlowMinLuminance</key>
     <map>
@@ -7982,7 +8376,7 @@
       <key>Type</key>
       <string>F32</string>
       <key>Value</key>
-      <real>2.5</real>
+      <real>9999</real>
     </map>
     <key>RenderGlowResolutionPow</key>
     <map>
@@ -8096,7 +8490,7 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
-      <integer>1</integer>
+      <integer>0</integer>
     </map>
     <key>RenderHideGroupTitle</key>
     <map>
@@ -8462,17 +8856,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
-    <key>RenderUseFBO</key>
-    <map>
-      <key>Comment</key>
-      <string>Whether we want to use GL_EXT_framebuffer_objects.</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>
+    <string>Use triangle strips for rendering prims.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
   <key>RenderUseTriStrips</key>
   <map>
     <key>Comment</key>
@@ -8561,7 +8955,18 @@
     <key>Value</key>
     <integer>1</integer>
   </map>
-    <key>RenderVolumeLODFactor</key>
+	<key>RenderPreferStreamDraw</key>
+	<map>
+		<key>Comment</key>
+		<string>Use GL_STREAM_DRAW in place of GL_DYNAMIC_DRAW</string>
+		<key>Persist</key>
+		<integer>1</integer>
+		<key>Type</key>
+		<string>Boolean</string>
+		<key>Value</key>
+		<integer>0</integer>
+	</map>
+	<key>RenderVolumeLODFactor</key>
     <map>
       <key>Comment</key>
       <string>Controls level of detail of primitives (multiplier for current screen area when calculated level of detail)</string>
@@ -8638,7 +9043,51 @@
       <key>Value</key>
       <real>1.0</real>
     </map>
-    <key>SafeMode</key>
+  <key>MeshStreamingCostScaler</key>
+  <map>
+    <key>Comment</key>
+    <string>DEBUG</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>3.0</real>
+  </map>
+  <key>MeshThreadCount</key>
+  <map>
+    <key>Comment</key>
+    <string>Number of threads to use for loading meshes.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>8</integer>
+  </map>
+  <key>MeshMaxConcurrentRequests</key>
+  <map>
+    <key>Comment</key>
+    <string>Number of threads to use for loading meshes.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>32</integer>
+  </map>
+   <key>RunMultipleThreads</key>
+    <map>
+      <key>Comment</key>
+      <string>If TRUE keep background threads active during render</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
+  <key>SafeMode</key>
     <map>
       <key>Comment</key>
       <string>Reset preferences, run in safe mode.</string>
@@ -9914,6 +10363,17 @@
       <key>Value</key>
       <string>pilot.txt</string>
     </map>
+    <key>StatsPilotXMLFile</key>
+    <map>
+      <key>Comment</key>
+      <string>Filename for stats logging extended autopilot path</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>pilot.xml</string>
+    </map>
     <key>StatsQuitAfterRuns</key>
     <map>
       <key>Comment</key>
@@ -11397,7 +11857,7 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
-      <integer>1</integer>
+      <integer>0</integer>
     </map>
     <key>SpeakerParticipantDefaultOrder</key>
     <map>
diff --git a/indra/newview/app_settings/settings_crash_behavior.xml b/indra/newview/app_settings/settings_crash_behavior.xml
index cc7f5ac88b5362720f37053c514ae5d99c5eab99..97651ff4ca49d58da6e029cc423a97c09bf024ce 100644
--- a/indra/newview/app_settings/settings_crash_behavior.xml
+++ b/indra/newview/app_settings/settings_crash_behavior.xml
@@ -9,7 +9,7 @@
         <key>Type</key>
             <string>S32</string>
         <key>Value</key>
-            <integer>0</integer>
+            <integer>1</integer>
         </map>
     </map>
 </llsd>
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
index 5de9cb07908055eef5e3aaf3d8e8ea5a75967fa1..3f6b8b33238fc1fd4b228575109fdd4eac825552 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void default_lighting();
 
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
index 7e9818e54a7f3e141cc67e530991a7a6948eba41..1ad87badfe17dfe8b21a8beecdbbe25a9d911c9d 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 attribute vec4 weight;  //1
 
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
index 9f06301cc7bb67bcda69fc51f9e0a10bff25e0b0..a15846f192f66b5356bec88cc4f3a38bf6100eb4 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 mat4 getSkinnedTransform();
diff --git a/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl b/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
index 0feb88535a94b4c3273a5281444507e96e19b7c3..05fe10037227c1826e430b14ec3af7cbb877ee7d 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void default_lighting();
 
diff --git a/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl b/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
index 30a2f10f62bf98bf9f29c41cb937e81b8a91ee19..4b8a7604a1b6ffdaa45b6077fc20b14bab0ad861 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec4 baseCol);
 void calcAtmospherics(vec3 inPositionEye);
diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..ef823c28b1c48693e7e8151fefe8baade88988cf
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl
@@ -0,0 +1,30 @@
+/** 
+ * @file objectSkinV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+attribute vec4 object_weight;  
+
+uniform mat4 matrixPalette[32];
+
+mat4 getObjectSkinnedTransform()
+{
+	int i; 
+	
+	vec4 w = fract(object_weight);
+	vec4 index = floor(object_weight);
+	
+	float scale = 1.0/(w.x+w.y+w.z+w.w);
+	w *= scale;
+	
+	mat4 mat = matrixPalette[int(index.x)]*w.x;
+	mat += matrixPalette[int(index.y)]*w.y;
+	mat += matrixPalette[int(index.z)]*w.z;
+	mat += matrixPalette[int(index.w)]*w.w;
+		
+	return mat;
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
index bcd710dc570fa87976c95b08f3d2bb01c616fd31..27ac59a840f53adf2bb805c9c324f2a5afd097b9 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
index 299def19272bdbd3cc1229a313487ce0503559d6..f1aa549a478c7798b99ae7088db26840ff07c232 100644
--- a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
+++ b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 mat4 getSkinnedTransform();
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl
index 171a0e76f70bafa54c59d136bbc19b06f34c2b5a..3b12a07a27df958a59de4e0dcc10e488b9bdb9ce 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl
@@ -4,11 +4,12 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
 uniform sampler2D diffuseMap;
-uniform sampler2D noiseMap;
 uniform sampler2DRect depthMap;
 
 uniform mat4 shadow_matrix[6];
@@ -22,7 +23,7 @@ varying vec3 vary_ambient;
 varying vec3 vary_directional;
 varying vec3 vary_fragcoord;
 varying vec3 vary_position;
-varying vec3 vary_light;
+varying vec3 vary_pointlight_col;
 
 uniform mat4 inv_proj;
 
@@ -44,18 +45,19 @@ void main()
 	vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5;
 	frag *= screen_res;
 	
-	vec3 samp_pos = getPosition(frag).xyz;
-	
 	vec4 pos = vec4(vary_position, 1.0);
 	
+	vec4 diff= texture2D(diffuseMap, gl_TexCoord[0].xy);
+
 	vec4 col = vec4(vary_ambient + vary_directional.rgb, gl_Color.a);
-	vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy) * col;
+	vec4 color = diff * col;
 	
 	color.rgb = atmosLighting(color.rgb);
 
 	color.rgb = scaleSoftClip(color.rgb);
 
-	//gl_FragColor = gl_Color;
+	color.rgb += diff.rgb * vary_pointlight_col.rgb;
+
 	gl_FragColor = color;
 	//gl_FragColor = vec4(1,0,1,1);
 	//gl_FragColor = vec4(1,0,1,1)*shadow;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..5addbbb1763ade3c49fd27dfdb8984578dbfb4f6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
@@ -0,0 +1,107 @@
+/** 
+ * @file alphaSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+ 
+#version 120
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+mat4 getObjectSkinnedTransform();
+void calcAtmospherics(vec3 inPositionEye);
+
+float calcDirectionalLight(vec3 n, vec3 l);
+
+vec3 atmosAmbient(vec3 light);
+vec3 atmosAffectDirectionalLight(float lightIntensity);
+vec3 scaleDownLight(vec3 light);
+vec3 scaleUpLight(vec3 light);
+
+varying vec3 vary_position;
+varying vec3 vary_ambient;
+varying vec3 vary_directional;
+varying vec3 vary_normal;
+varying vec3 vary_fragcoord;
+varying vec3 vary_pointlight_col;
+
+uniform float near_clip;
+
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight)
+{
+	//get light vector
+	vec3 lv = lp.xyz-v;
+	
+	//get distance
+	float d = length(lv);
+	
+	//normalize light vector
+	lv *= 1.0/d;
+	
+	//distance attenuation
+	float dist2 = d*d/(la*la);
+	float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+
+	// spotlight coefficient.
+	float spot = max(dot(-ln, lv), is_pointlight);
+	da *= spot*spot; // GL_SPOT_EXPONENT=2
+
+	//angular attenuation
+	da *= calcDirectionalLight(n, lv);
+
+	return da;	
+}
+
+void main()
+{
+	gl_TexCoord[0] = gl_MultiTexCoord0;
+				
+	vec4 pos;
+	vec3 norm;
+	
+	mat4 trans = getObjectSkinnedTransform();
+	trans = gl_ModelViewMatrix * trans;
+	
+	pos = trans * gl_Vertex;
+	
+	norm = gl_Vertex.xyz + gl_Normal.xyz;
+	norm = normalize(( trans*vec4(norm, 1.0) ).xyz-pos.xyz);
+	
+	vec4 frag_pos = gl_ProjectionMatrix * pos;
+	gl_Position = frag_pos;
+	
+	vary_position = pos.xyz;
+	vary_normal = norm;	
+	
+	calcAtmospherics(pos.xyz);
+
+	vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
+
+	// Collect normal lights
+	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a);
+	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a);
+	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a);
+	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a);
+	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a);
+	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a);
+	
+	vary_pointlight_col = col.rgb*gl_Color.rgb;
+
+	col.rgb = vec3(0,0,0);
+
+	// Add windlight lights
+	col.rgb = atmosAmbient(vec3(0.));
+	
+	vary_ambient = col.rgb*gl_Color.rgb;
+	vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
+	
+	col.rgb = min(col.rgb*gl_Color.rgb, 1.0);
+	
+	gl_FrontColor = col;
+
+	gl_FogFragCoord = pos.z;
+	
+	vary_fragcoord.xyz = frag_pos.xyz + vec3(0,0,near_clip);
+}
+
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
index fabbce08246f1d82f67e5ffcfdf16dd86838b863..525b68c43793a499514e4057fbca9c0e4fecd647 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
@@ -5,11 +5,12 @@
  * $/LicenseInfo$
  */
 
+#version 120
+
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 void calcAtmospherics(vec3 inPositionEye);
 
 float calcDirectionalLight(vec3 n, vec3 l);
-float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
 
 vec3 atmosAmbient(vec3 light);
 vec3 atmosAffectDirectionalLight(float lightIntensity);
@@ -21,11 +22,37 @@ varying vec3 vary_directional;
 varying vec3 vary_fragcoord;
 varying vec3 vary_position;
 varying vec3 vary_light;
+varying vec3 vary_pointlight_col;
 
 uniform float near_clip;
 uniform float shadow_offset;
 uniform float shadow_bias;
 
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight)
+{
+	//get light vector
+	vec3 lv = lp.xyz-v;
+	
+	//get distance
+	float d = length(lv);
+	
+	//normalize light vector
+	lv *= 1.0/d;
+	
+	//distance attenuation
+	float dist2 = d*d/(la*la);
+	float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+
+	// spotlight coefficient.
+	float spot = max(dot(-ln, lv), is_pointlight);
+	da *= spot*spot; // GL_SPOT_EXPONENT=2
+
+	//angular attenuation
+	da *= calcDirectionalLight(n, lv);
+
+	return da;	
+}
+
 void main()
 {
 	//transform vertex
@@ -36,33 +63,35 @@ void main()
 	vec4 pos = (gl_ModelViewMatrix * gl_Vertex);
 	vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
 	
-	vary_position = pos.xyz + norm.xyz * (-pos.z/64.0*shadow_offset+shadow_bias);
+	float dp_directional_light = max(0.0, dot(norm, gl_LightSource[0].position.xyz));
+	vary_position = pos.xyz + gl_LightSource[0].position.xyz * (1.0-dp_directional_light)*shadow_offset;
 		
 	calcAtmospherics(pos.xyz);
 
 	//vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.));
-
 	vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
 
-	// Collect normal lights (need to be divided by two, as we later multiply by 2)
-	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a);
-	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a);
-	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a);
-	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a);
-	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a);
-	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a);
-	col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
-	col.rgb = scaleDownLight(col.rgb);
+	// Collect normal lights
+	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a);
+	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a);
+	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a);
+	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a);
+	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a);
+	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a);
 	
+	vary_pointlight_col = col.rgb*gl_Color.rgb;
+
+	col.rgb = vec3(0,0,0);
+
 	// Add windlight lights
-	col.rgb += atmosAmbient(vec3(0.));
+	col.rgb = atmosAmbient(vec3(0.));
 	
 	vary_light = gl_LightSource[0].position.xyz;
 	
 	vary_ambient = col.rgb*gl_Color.rgb;
 	vary_directional.rgb = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
 	
-	col.rgb = min(col.rgb*gl_Color.rgb, 1.0);
+	col.rgb = col.rgb*gl_Color.rgb;
 	
 	gl_FrontColor = col;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..164322c3a732a87e16d84edd104876370e095931
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl
@@ -0,0 +1,18 @@
+/** 
+ * @file avatarShadowF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+uniform sampler2D diffuseMap;
+
+
+void main() 
+{
+	//gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a);
+	gl_FragColor = vec4(1,1,1,1);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..5ae41cb73093d99f0e6c5ba60f3c109729057c6d
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl
@@ -0,0 +1,27 @@
+/** 
+ * @file attachmentShadowV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+mat4 getObjectSkinnedTransform();
+
+void main()
+{
+	//transform vertex
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	gl_FrontColor = gl_Color;
+	
+	vec4 p = gl_ProjectionMatrix * vec4(pos, 1.0);
+	p.z = max(p.z, -p.w+0.01);
+	gl_Position = p;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaF.glsl
deleted file mode 100644
index 82ce6d7377ed9183e1521315b9d240340035f101..0000000000000000000000000000000000000000
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaF.glsl
+++ /dev/null
@@ -1,68 +0,0 @@
-/** 
- * @file avatarAlphaF.glsl
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * $/LicenseInfo$
- */
-
-uniform sampler2D diffuseMap;
-uniform sampler2DShadow shadowMap0;
-uniform sampler2DShadow shadowMap1;
-uniform sampler2DShadow shadowMap2;
-uniform sampler2DShadow shadowMap3;
-uniform sampler2D noiseMap;
-
-uniform mat4 shadow_matrix[6];
-uniform vec4 shadow_clip;
-
-vec3 atmosLighting(vec3 light);
-vec3 scaleSoftClip(vec3 light);
-
-varying vec3 vary_ambient;
-varying vec3 vary_directional;
-varying vec4 vary_position;
-varying vec3 vary_normal;
-
-void main() 
-{
-	float shadow = 1.0;
-	vec4 pos = vary_position;
-	vec3 norm = normalize(vary_normal);
-	
-	vec3 nz = texture2D(noiseMap, gl_FragCoord.xy/128.0).xyz;
-
-	if (pos.z > -shadow_clip.w)
-	{	
-		
-		if (pos.z < -shadow_clip.z)
-		{
-			vec4 lpos = shadow_matrix[3]*pos;
-			shadow = shadow2DProj(shadowMap3, lpos).x;
-		}
-		else if (pos.z < -shadow_clip.y)
-		{
-			vec4 lpos = shadow_matrix[2]*pos;
-			shadow = shadow2DProj(shadowMap2, lpos).x;
-		}
-		else if (pos.z < -shadow_clip.x)
-		{
-			vec4 lpos = shadow_matrix[1]*pos;
-			shadow = shadow2DProj(shadowMap1, lpos).x;
-		}
-		else
-		{
-			vec4 lpos = shadow_matrix[0]*pos;
-			shadow = shadow2DProj(shadowMap0, lpos).x;
-		}
-	}
-	
-	
-	vec4 col = vec4(vary_ambient + vary_directional*shadow, gl_Color.a);	
-	vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy) * col;
-	
-	color.rgb = atmosLighting(color.rgb);
-
-	color.rgb = scaleSoftClip(color.rgb);
-
-	gl_FragColor = color;
-}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl
index 21ddc2fad8473961e99374949940b22a71e31e81..a2a7dea20d5cf6206d564605e01401f2e29804c3 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 mat4 getSkinnedTransform();
@@ -17,10 +19,38 @@ vec3 atmosAffectDirectionalLight(float lightIntensity);
 vec3 scaleDownLight(vec3 light);
 vec3 scaleUpLight(vec3 light);
 
-varying vec4 vary_position;
+varying vec3 vary_position;
 varying vec3 vary_ambient;
 varying vec3 vary_directional;
-varying vec3 vary_normal;
+varying vec3 vary_fragcoord;
+varying vec3 vary_pointlight_col;
+
+uniform float near_clip;
+
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight)
+{
+	//get light vector
+	vec3 lv = lp.xyz-v;
+	
+	//get distance
+	float d = length(lv);
+	
+	//normalize light vector
+	lv *= 1.0/d;
+	
+	//distance attenuation
+	float dist2 = d*d/(la*la);
+	float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+
+	// spotlight coefficient.
+	float spot = max(dot(-ln, lv), is_pointlight);
+	da *= spot*spot; // GL_SPOT_EXPONENT=2
+
+	//angular attenuation
+	da *= calcDirectionalLight(n, lv);
+
+	return da;	
+}
 
 void main()
 {
@@ -40,9 +70,10 @@ void main()
 	norm.z = dot(trans[2].xyz, gl_Normal);
 	norm = normalize(norm);
 		
-	gl_Position = gl_ProjectionMatrix * pos;
-	vary_position = pos;
-	vary_normal = norm;	
+	vec4 frag_pos = gl_ProjectionMatrix * pos;
+	gl_Position = frag_pos;
+	
+	vary_position = pos.xyz;
 	
 	calcAtmospherics(pos.xyz);
 
@@ -50,18 +81,20 @@ void main()
 
 	vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
 
-	// Collect normal lights (need to be divided by two, as we later multiply by 2)
-	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a);
-	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a);
-	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a);
-	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a);
-	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a);
-	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a);
-	col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
-	col.rgb = scaleDownLight(col.rgb);
+	// Collect normal lights
+	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a);
+	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a);
+	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a);
+	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a);
+	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a);
+	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a);
 	
+	vary_pointlight_col = col.rgb*gl_Color.rgb;
+
+	col.rgb = vec3(0,0,0);
+
 	// Add windlight lights
-	col.rgb += atmosAmbient(vec3(0.));
+	col.rgb = atmosAmbient(vec3(0.));
 	
 	vary_ambient = col.rgb*gl_Color.rgb;
 	vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
@@ -71,7 +104,8 @@ void main()
 	gl_FrontColor = col;
 
 	gl_FogFragCoord = pos.z;
-
+	
+	vary_fragcoord.xyz = frag_pos.xyz + vec3(0,0,near_clip);
 }
 
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
index e376892e0a8e6d9e35b619f5b9f497b1fa7f2615..9748727147d62107ee9ed3f708852446d785cacc 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl
index d88e3ecfd8d7b544704e3de409b6bafa4a9e4142..1b7ae0688841b60691e279a18efd5f6d3baa9ec8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl
index 2af8c8f5f799587730510d82189eb7ea0ed30181..cf6579a40d4c608193da404644ad33ba1594fa9c 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 mat4 getSkinnedTransform();
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl
index 988226fb7c15856ec420f53a1cf2c4c4b271e7c5..69c93799b5d358b612c5be0a18150c6b193fb777 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 mat4 getSkinnedTransform();
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
index 258a9b7c400b078870f959b32de85be3b95bcf72..d9f021b114f4919327c70f62c967750f98887d22 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
@@ -37,44 +39,49 @@ vec4 getPosition(vec2 pos_screen)
 
 void main() 
 {
-	vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz;
+        vec2 tc = vary_fragcoord.xy;
+	vec3 norm = texture2DRect(normalMap, tc).xyz;
 	norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
-	vec3 pos = getPosition(vary_fragcoord.xy).xyz;
-	vec4 ccol = texture2DRect(lightMap, vary_fragcoord.xy).rgba;
+	vec3 pos = getPosition(tc).xyz;
+	vec4 ccol = texture2DRect(lightMap, tc).rgba;
 	
 	vec2 dlt = kern_scale * delta / (1.0+norm.xy*norm.xy);
-	
 	dlt /= max(-pos.z*dist_factor, 1.0);
 	
 	vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free'
 	vec4 col = defined_weight.xyxx * ccol;
-	
+
+	// relax tolerance according to distance to avoid speckling artifacts, as angles and distances are a lot more abrupt within a small screen area at larger distances
+	float pointplanedist_tolerance_pow2 = pos.z*pos.z*0.00005;
+
+	// perturb sampling origin slightly in screen-space to hide edge-ghosting artifacts where smoothing radius is quite large
+	tc += ( (mod(tc.x+tc.y,2) - 0.5) * kern[1].z * dlt * 0.5 );
+
 	for (int i = 1; i < 4; i++)
 	{
-		vec2 tc = vary_fragcoord.xy + kern[i].z*dlt;
-	        vec3 samppos = getPosition(tc).xyz; 
+		vec2 samptc = tc + kern[i].z*dlt;
+	        vec3 samppos = getPosition(samptc).xyz; 
 		float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
-		if (d*d <= 0.003)
+		if (d*d <= pointplanedist_tolerance_pow2)
 		{
-			col += texture2DRect(lightMap, tc)*kern[i].xyxx;
+			col += texture2DRect(lightMap, samptc)*kern[i].xyxx;
 			defined_weight += kern[i].xy;
 		}
 	}
 	for (int i = 1; i < 4; i++)
 	{
-		vec2 tc = vary_fragcoord.xy - kern[i].z*dlt;
-	        vec3 samppos = getPosition(tc).xyz; 
+		vec2 samptc = tc - kern[i].z*dlt;
+	        vec3 samppos = getPosition(samptc).xyz; 
 		float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
-		if (d*d <= 0.003)
+		if (d*d <= pointplanedist_tolerance_pow2)
 		{
-			col += texture2DRect(lightMap, tc)*kern[i].xyxx;
+			col += texture2DRect(lightMap, samptc)*kern[i].xyxx;
 			defined_weight += kern[i].xy;
 		}
 	}
 
-
-
 	col /= defined_weight.xyxx;
+	col.y *= col.y;
 	
 	gl_FragColor = col;
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightV.glsl
index b1b3f55f0065d75425b82088c198a6d31b20d27a..c2d05c601a8cf9b760c5c0d4bdbc302619000055 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/blurLightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
index 35f334d58e0f3a3f200b97727b690784be247942..37bfaac32cbf49f49aec497f0aee6d689010f5ff 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform sampler2D bumpMap;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..d884f2e4a5de3b066518ddba72758c0bd05b912a
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
@@ -0,0 +1,37 @@
+/** 
+ * @file bumpV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+varying vec3 vary_mat0;
+varying vec3 vary_mat1;
+varying vec3 vary_mat2;
+
+mat4 getObjectSkinnedTransform();
+
+void main()
+{
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	
+	vec3 n = normalize((mat * vec4(gl_Normal.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz);
+	vec3 b = normalize((mat * vec4(gl_MultiTexCoord2.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz);
+	vec3 t = cross(b, n);
+	
+	vary_mat0 = vec3(t.x, b.x, n.x);
+	vary_mat1 = vec3(t.y, b.y, n.y);
+	vary_mat2 = vec3(t.z, b.z, n.z);
+	
+	gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+	gl_FrontColor = gl_Color;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
index 6c8550cb5bdc2580e3d4df670581d5867ee166da..9b109b2db67ec589d752f18cd8c3103e67df2138 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_mat0;
 varying vec3 vary_mat1;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl
index 9bd622a506a4c207457308b939c23d97ad1e6d3f..35cfb80c93882795211bd29c2834427513ac0a2b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..9a45c03237a885a54958c239dafbc473f9ec56e9
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl
@@ -0,0 +1,33 @@
+/** 
+ * @file diffuseSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+varying vec3 vary_normal;
+
+mat4 getObjectSkinnedTransform();
+
+void main()
+{
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	vec4 norm = gl_Vertex;
+	norm.xyz += gl_Normal.xyz;
+	norm.xyz = (mat*norm).xyz;
+	norm.xyz = normalize(norm.xyz-pos.xyz);
+
+	vary_normal = norm.xyz;
+			
+	gl_FrontColor = gl_Color;
+	
+	gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
index bd580963173cc6b7da58e1d6d247735badfbd32b..03d3322cb68855adfc5a7c9fe0de09b73ae96a19 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_normal;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
index f53e15c6cc018f8ac9d6fb805236b22f47af82a7..34298773974b2d35ea7396dceaebf3b7e1d363b9 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl
index dc8b2c6be47adb0e7024ea02e570da7bf4696e78..6c38d220e26962bf7ad251ca51fcc3f7e456c0d0 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/giF.glsl b/indra/newview/app_settings/shaders/class1/deferred/giF.glsl
index e64e29a0d2ad1dc7213531b505dbda5cd0dfb001..75b555e8ae35eb709428304f60e9a16836938f6d 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/giF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/giF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/giV.glsl b/indra/newview/app_settings/shaders/class1/deferred/giV.glsl
index 543527612ee540f8558eb9d63a928c9af443ff93..8dc1410ea5ad90d05d8bad4c485238550fd4cdd7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/giV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/giV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl b/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl
index 7f365fedc8f563a0104aab6293384d03db7d31d8..e3c15a2ab2d668bc15bdc2649c0f98ae1d585222 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform sampler2D normalMap;
@@ -12,7 +14,7 @@ uniform sampler2D specularMap;
 void main() 
 {
 	vec4 col = texture2D(diffuseMap, gl_TexCoord[0].xy);
-	gl_FragData[0] = vec4(col.rgb, col.a <= 0.5 ? 0.0 : 0.005);
+	gl_FragData[0] = vec4(col.rgb, col.a * 0.005);
 	gl_FragData[1] = texture2D(specularMap, gl_TexCoord[0].xy);
 	gl_FragData[2] = vec4(texture2D(normalMap, gl_TexCoord[0].xy).xyz, 0.0);
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/impostorV.glsl b/indra/newview/app_settings/shaders/class1/deferred/impostorV.glsl
index 4fc27d4412570bc6099c91f7889a50eef10c6a64..37148b3f1a8c48c036290a9b2edc431ab5e61a9e 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/impostorV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/impostorV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void main()
 {
diff --git a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
index acb3014d18ed5bd31663dce5cbcf5e07e11aca8e..78df54d5dc2b3726ad28daa881f3ceb1b9eebc8f 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
@@ -5,6 +5,8 @@
  * $/LicenseInfo$
  */
 
+#version 120
+
 uniform sampler2DRect diffuseMap;
 
 varying vec2 vary_fragcoord;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/luminanceV.glsl b/indra/newview/app_settings/shaders/class1/deferred/luminanceV.glsl
index 6368def830087fb15ea4fe33491f9fa2f7d85e96..0c820bfc6cf314aed20c9049b255953aac8b7f4f 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/luminanceV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/luminanceV.glsl
@@ -5,6 +5,9 @@
  * $/LicenseInfo$
  */
 
+#version 120
+
+
 varying vec2 vary_fragcoord;
 
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
index 6fca08ae6aa6b2219a897c05bbd359959c7d32a9..c5ddf31ac03b727b5e4370b0fd64e199ce2c52cd 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
@@ -5,6 +5,8 @@
  * $/LicenseInfo$
  */
 
+#version 120
+
 #extension GL_ARB_texture_rectangle : enable
 
 uniform sampler2DRect depthMap;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/blurLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightV.glsl
similarity index 52%
rename from indra/newview/app_settings/shaders/class2/deferred/blurLightV.glsl
rename to indra/newview/app_settings/shaders/class1/deferred/multiPointLightV.glsl
index b1b3f55f0065d75425b82088c198a6d31b20d27a..2e3e84dd156fd62563fbd241cd02d38f9236fb50 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/blurLightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightV.glsl
@@ -1,17 +1,20 @@
 /** 
- * @file blurLightF.glsl
+ * @file multiPointLightV.glsl
  *
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
 
-varying vec2 vary_fragcoord;
-uniform vec2 screen_res;
+#version 120
+
+varying vec4 vary_fragcoord;
 
 void main()
 {
 	//transform vertex
-	gl_Position = ftransform(); 
 	vec4 pos = gl_ModelViewProjectionMatrix * gl_Vertex;
-	vary_fragcoord = (pos.xy*0.5+0.5)*screen_res;
+	vary_fragcoord = pos;
+
+	gl_Position = pos;
+	gl_FrontColor = gl_Color;
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
index 43da16436b5d27d39c766db8988ab8e79d662968..22ed9dcd40ec25f456f91c5dc0e92333e41dfb27 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+ #version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl
index e056c3e8963a9eba7f2ba89ea569a3df8fb7d467..8e74feb61565cac9f6d4736bc04c72909a293867 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl
@@ -5,6 +5,8 @@
  * $/LicenseInfo$
  */
 
+#version 120
+
 varying vec4 vary_light;
 varying vec4 vary_fragcoord;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
index 650e1a91a863f97775fa5dd701d4f434093de43b..77f1b2224c20f04f66b79b948fc5be3a3844f6ea 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
@@ -4,54 +4,134 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
 uniform sampler2DRect diffuseRect;
-uniform sampler2DRect localLightMap;
-uniform sampler2DRect sunLightMap;
-uniform sampler2DRect giLightMap;
-uniform sampler2D	  luminanceMap;
-uniform sampler2DRect lightMap;
+uniform sampler2DRect edgeMap;
+uniform sampler2DRect depthMap;
+uniform sampler2DRect normalMap;
+uniform sampler2D bloomMap;
 
-uniform vec3 lum_quad;
-uniform float lum_lod;
-uniform vec4 ambient;
-
-uniform vec3 gi_quad;
+uniform float depth_cutoff;
+uniform float norm_cutoff;
+uniform float focal_distance;
+uniform float blur_constant;
+uniform float tan_pixel_angle;
+uniform float magnification;
 
+uniform mat4 inv_proj;
 uniform vec2 screen_res;
+
 varying vec2 vary_fragcoord;
 
-void main() 
+float getDepth(vec2 pos_screen)
 {
-	vec2 tc = vary_fragcoord.xy;
-	vec3 lum = texture2DLod(luminanceMap, tc/screen_res, lum_lod).rgb;
-	float luminance = lum.r;
-	luminance = luminance*lum_quad.y+lum_quad.z;
+	float z = texture2DRect(depthMap, pos_screen.xy).a;
+	z = z*2.0-1.0;
+	vec4 ndc = vec4(0.0, 0.0, z, 1.0);
+	vec4 p = inv_proj*ndc;
+	return p.z/p.w;
+}
 
-	vec4 diff = texture2DRect(diffuseRect, vary_fragcoord.xy);
+float calc_cof(float depth)
+{
+	float sc = abs(depth-focal_distance)/-depth*blur_constant;
+		
+	sc /= magnification;
+	
+	// tan_pixel_angle = pixel_length/-depth;
+	float pixel_length =  tan_pixel_angle*-focal_distance;
+	
+	sc = sc/pixel_length;
+	sc *= 1.414;
+	
+	return sc;
+}
 
-	float ambocc = texture2DRect(lightMap, vary_fragcoord.xy).g;
-			
-	vec3 gi_col = texture2DRect(giLightMap, vary_fragcoord.xy).rgb;
-	gi_col = gi_col*gi_col*gi_quad.x + gi_col*gi_quad.y+gi_quad.z*ambocc*ambient.rgb;
-	gi_col *= diff;
+void dofSampleNear(inout vec4 diff, inout float w, float cur_sc, vec2 tc)
+{
+	float d = getDepth(tc);
 	
-	vec4 sun_col =	texture2DRect(sunLightMap, vary_fragcoord.xy);
+	float sc = calc_cof(d);
 	
-	vec3 local_col = texture2DRect(localLightMap, vary_fragcoord.xy).rgb;
+	float wg = 0.25;
 		
+	vec4 s = texture2DRect(diffuseRect, tc);
+	// de-weight dull areas to make highlights 'pop'
+	wg += s.r+s.g+s.b;
+	
+	diff += wg*s;
+	
+	w += wg;
+}
+
+void dofSample(inout vec4 diff, inout float w, float min_sc, float cur_depth, vec2 tc)
+{
+	float d = getDepth(tc);
+	
+	float sc = calc_cof(d);
+	
+	if (sc > min_sc //sampled pixel is more "out of focus" than current sample radius
+	   || d < cur_depth) //sampled pixel is further away than current pixel
+	{
+		float wg = 0.25;
 		
-	sun_col *= 1.0/min(luminance, 1.0);
-	gi_col *= 1.0/luminance;
+		vec4 s = texture2DRect(diffuseRect, tc);
+		// de-weight dull areas to make highlights 'pop'
+		wg += s.r+s.g+s.b;
+	
+		diff += wg*s;
+		
+		w += wg;
+	}
+}
+
+
+void main() 
+{
+	vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz;
+	norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
 		
-	vec3 col = sun_col.rgb+gi_col+local_col;
+	vec2 tc = vary_fragcoord.xy;
 	
-	gl_FragColor.rgb = col.rgb;
-	col.rgb = max(col.rgb-vec3(1.0,1.0,1.0), vec3(0.0, 0.0, 0.0)); 
+	float depth = getDepth(tc);
 	
-	gl_FragColor.a = 0.0; // max(dot(col.rgb,col.rgb)*lum_quad.x, sun_col.a);
+	vec4 diff = texture2DRect(diffuseRect, vary_fragcoord.xy);
 	
-	//gl_FragColor.rgb = vec3(lum_lod);
+	{ 
+		float w = 1.0;
+		
+		float sc = calc_cof(depth);
+		sc = min(abs(sc), 10.0);
+		
+		float fd = depth*0.5f;
+		
+		float PI = 3.14159265358979323846264;
+
+		// sample quite uniformly spaced points within a circle, for a circular 'bokeh'		
+		//if (depth < focal_distance)
+		{
+			while (sc > 0.5)
+			{
+				int its = int(max(1.0,(sc*3.7)));
+				for (int i=0; i<its; ++i)
+				{
+					float ang = sc+i*2*PI/its; // sc is added for rotary perturbance
+					float samp_x = sc*sin(ang);
+					float samp_y = sc*cos(ang);
+					// you could test sample coords against an interesting non-circular aperture shape here, if desired.
+					dofSample(diff, w, sc, depth, vary_fragcoord.xy + vec2(samp_x,samp_y));
+				}
+				sc -= 1.0;
+			}
+		}
+		
+		diff /= w;
+	}
+		
+	vec4 bloom = texture2D(bloomMap, vary_fragcoord.xy/screen_res);
+	gl_FragColor = diff + bloom;
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..ab48d08bbb070342a2f48402b32859c8aaedf01a
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl
@@ -0,0 +1,24 @@
+/** 
+ * @file postDeferredF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+ 
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+
+uniform sampler2DRect diffuseRect;
+uniform sampler2D bloomMap;
+
+uniform vec2 screen_res;
+varying vec2 vary_fragcoord;
+
+void main() 
+{
+	vec4 diff = texture2DRect(diffuseRect, vary_fragcoord.xy);
+	
+	vec4 bloom = texture2D(bloomMap, vary_fragcoord.xy/screen_res);
+	gl_FragColor = diff + bloom;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredV.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredV.glsl
index 0ec81dcb02ed61b19b8b93d6dd0356a00171a584..12983baa94882bfc3f58349847dd7bec1b3aadca 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl
index e8e58f50e1c8650e71f1d4dc81393a8ed56bc76f..63b3c9f20527dae3364ede4002f395aedf798ae4 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect depthMap;
 uniform sampler2DRect normalMap;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postgiV.glsl b/indra/newview/app_settings/shaders/class1/deferred/postgiV.glsl
index e5f621764413d68a121116dc890d7d938a363fc1..ae57227fe528b03b5e2a0d216c9a8b97a5ddfb39 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postgiV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postgiV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowF.glsl
index 378a3295ec2e38af28e0ec95732a050b50cf2811..6674c4a5aa275a379c2bab374801a9b38ebe51f9 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/shadowF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/shadowF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowV.glsl
index 666f909f0172b2e885460d2ca5a1a3eb6613a777..db3bddc6bea4b635bb834b2a59b29f00001ba503 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/shadowV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/shadowV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec4 post_pos;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
index 5fbeceba81c0af08cf302a90cc17e613a8b4a171..29340c7e9f01bc8d44ad910fe56c4d0e243a6a4d 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
@@ -268,14 +270,10 @@ void main()
 	vec4 diffuse = texture2DRect(diffuseRect, tc);
 	vec4 spec = texture2DRect(specularRect, vary_fragcoord.xy);
 	
-	vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg;
-	float scol = max(scol_ambocc.r, diffuse.a); 
-	float ambocc = scol_ambocc.g;
-	
-	calcAtmospherics(pos.xyz, ambocc);
+	calcAtmospherics(pos.xyz, 1.0);
 	
 	vec3 col = atmosAmbient(vec3(0));
-	col += atmosAffectDirectionalLight(max(min(da, scol), diffuse.a));
+	col += atmosAffectDirectionalLight(max(min(da, 1.0), diffuse.a));
 	
 	col *= diffuse.rgb;
 	
@@ -285,7 +283,7 @@ void main()
 		//
 		vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
 		float sa = dot(refnormpersp, vary_light.xyz);
-		vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a;
+		vec3 dumbshiny = vary_SunlitColor*texture2D(lightFunc, vec2(sa, spec.a)).a;
 
 		/*
 		// screen-space cheap fakey reflection map
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightV.glsl
index 9d187b46e208ba0b70005c661fc342ec013bd271..8f0bcca76b86a22f32101d0c81b3631110fff0c9 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/softenLightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec2 screen_res;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl
index d4d686bbb715b9be7e9445e29e22a1e5a413eaec..00093836a25472d9a1fa6ee545d04451d37c6e73 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 //class 1, no shadow, no SSAO, should never be called
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl
index cdbed4b7917a529f9dcf73dffd107de53508a917..cd91351ad411e91b425f478e65f6123f583ea01b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl
@@ -4,6 +4,8 @@
  * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
  * $License$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
@@ -13,8 +15,6 @@ uniform sampler2DRect depthMap;
 uniform sampler2DRect normalMap;
 uniform sampler2D noiseMap;
 
-uniform sampler2D		lightFunc;
-
 
 // Inputs
 uniform mat4 shadow_matrix[6];
@@ -51,57 +51,49 @@ float calcAmbientOcclusion(vec4 pos, vec3 norm)
 {
 	float ret = 1.0;
 	
-	float dist = dot(pos.xyz,pos.xyz);
-	
-	if (dist < 64.0*64.0)
-	{
-		vec2 kern[8];
-		// exponentially (^2) distant occlusion samples spread around origin
-		kern[0] = vec2(-1.0, 0.0) * 0.125*0.125;
-		kern[1] = vec2(1.0, 0.0) * 0.250*0.250;
-		kern[2] = vec2(0.0, 1.0) * 0.375*0.375;
-		kern[3] = vec2(0.0, -1.0) * 0.500*0.500;
-		kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625;
-		kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
-		kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
-		kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
+	vec2 kern[8];
+	// exponentially (^2) distant occlusion samples spread around origin
+	kern[0] = vec2(-1.0, 0.0) * 0.125*0.125;
+	kern[1] = vec2(1.0, 0.0) * 0.250*0.250;
+	kern[2] = vec2(0.0, 1.0) * 0.375*0.375;
+	kern[3] = vec2(0.0, -1.0) * 0.500*0.500;
+	kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625;
+	kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
+	kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
+	kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
 
-		vec2 pos_screen = vary_fragcoord.xy;
-		vec3 pos_world = pos.xyz;
-		vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy;
+	vec2 pos_screen = vary_fragcoord.xy;
+	vec3 pos_world = pos.xyz;
+	vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy;
 		
-		float angle_hidden = 0.0;
-		int points = 0;
+	float angle_hidden = 0.0;
+	int points = 0;
 		
-		float scale = min(ssao_radius / -pos_world.z, ssao_max_radius);
+	float scale = min(ssao_radius / -pos_world.z, ssao_max_radius);
 		
-		// it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?)
-		for (int i = 0; i < 8; i++)
-		{
-			vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect);
-			vec3 samppos_world = getPosition(samppos_screen).xyz; 
+	// it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations unrolling?)
+	for (int i = 0; i < 8; i++)
+	{
+		vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect);
+		vec3 samppos_world = getPosition(samppos_screen).xyz; 
 			
-			vec3 diff = pos_world - samppos_world;
-			float dist2 = dot(diff, diff);
+		vec3 diff = pos_world - samppos_world;
+		float dist2 = dot(diff, diff);
 			
-			// assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area
-			// --> solid angle shrinking by the square of distance
-			//radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2
-			//(k should vary inversely with # of samples, but this is taken care of later)
+		// assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area
+		// --> solid angle shrinking by the square of distance
+		//radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2
+		//(k should vary inversely with # of samples, but this is taken care of later)
 			
-			//if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0)  // -0.05*norm to shift sample point back slightly for flat surfaces
-			//	angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional.  max of 1.0 (= ssao_factor_inv * ssao_factor)
-			angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv);
+		angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv);
 			
-			// 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" 
-			points = points + int(diff.z > -1.0);
-		}
+		// 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" 
+		points = points + int(diff.z > -1.0);
+	}
 		
-		angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0);
+	angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0);
 		
-		ret = (1.0 - (float(points != 0) * angle_hidden));
-		ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0);
-	}
+	ret = (1.0 - (float(points != 0) * angle_hidden));
 	
 	return min(ret, 1.0);
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightV.glsl
index 9d092d9cea4cd340896a0fc0aa7c0e3642db4060..9beb513ad8a63a129b231381cb010e69c58b9510 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/sunLightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec4 vary_light;
 varying vec2 vary_fragcoord;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
index 9ba508a30cb700332bbdff89973454160705dc0b..0edae4791826e05939f027c54e6e2d64f05b6a7f 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D detail_0;
 uniform sampler2D detail_1;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
index 789e53b789b94ae9b7e9349cd61672f738f1d990..a6163063bebecc5c23d812122b66c7b6b7ef52e8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_normal;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
index 1c1725a95c581f3612fb2c60bac3ec6f1fe59951..c54d9a1e3e80dcf0bc9ba20d70c92b346d4fa3dc 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl
index 45832e350faec2f79e2a1728bd12f7e27b0c20e2..29689ecbafea503e78ef8c2413ed027c1e357885 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_normal;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl
index ea531de24ab391d41ec37eb5a331207bc549ab15..e76f598d09de4cd973e867dd439901954cc4475b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterV.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterV.glsl
index e002d75ebea049330f22b53eeef594b14f34bac1..649e3926300d27cf2a547230910f5a8dfba0c172 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/waterV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/waterV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
index 2d40a19fa66cea128faabc5a7624664584ad6792..f2023fa5ea1a76d510ffdaaa19e70acfa63078e8 100644
--- a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
+++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractV.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractV.glsl
index fe45898ed2e73b3bd234da7dee4e2b8095624d20..0ca0608b45aefebae7f54ce1016af2ead16223a5 100644
--- a/indra/newview/app_settings/shaders/class1/effects/glowExtractV.glsl
+++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractV.glsl
@@ -5,6 +5,7 @@
  * $/LicenseInfo$
  */
 
+#version 120
 
 void main() 
 {
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowF.glsl
index 5b4e8b3eccacb45f7b64cbbb48e2a91103cd5f46..65fc2e9f99bc9b3bc9036c3658dfb93a51498467 100644
--- a/indra/newview/app_settings/shaders/class1/effects/glowF.glsl
+++ b/indra/newview/app_settings/shaders/class1/effects/glowF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform float glowStrength;
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowV.glsl b/indra/newview/app_settings/shaders/class1/effects/glowV.glsl
index 97696e47194a0cd2d0126dea236504638369f2bb..0bd44cec903643cb7715b843524663886912e073 100644
--- a/indra/newview/app_settings/shaders/class1/effects/glowV.glsl
+++ b/indra/newview/app_settings/shaders/class1/effects/glowV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec2 glowDelta;
 
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
index 3a852239fb5a933ab359c7ba5522a4be07a8eda4..ac00f15b35b830f63cf6f11975994f261a08461d 100644
--- a/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D detail0;
 uniform sampler2D detail1;
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
index 0d781fd84904986e690915b8a79b4f700a7e4b7a..1e19ee7699f4e97fa046b6092ea1e700f582b53b 100644
--- a/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainWaterF.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainWaterF.glsl
index 99c340d91aeb7b878992cd0157e134bc8d7dfdfb..34f78565a5c3746d5de909709da4b918249d46a3 100644
--- a/indra/newview/app_settings/shaders/class1/environment/terrainWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 // this class1 shader is just a copy of terrainF
 
diff --git a/indra/newview/app_settings/shaders/class1/environment/underWaterF.glsl b/indra/newview/app_settings/shaders/class1/environment/underWaterF.glsl
index 66458ec66d1b6ce6d8f2537c4a2761e493c3906d..0dfac84a6e1928590de0cb72dc218f877c8ebb7f 100644
--- a/indra/newview/app_settings/shaders/class1/environment/underWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/underWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform sampler2D bumpMap;   
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterF.glsl b/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
index 5f1fbee1df9e2564df68c0c83cdc4cef18080034..4e9c09b1ea7a6dcf13fe07d22e24bb4311329dd5 100644
--- a/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec3 scaleSoftClip(vec3 inColor);
 vec3 atmosTransport(vec3 inColor);
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl b/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl
index e5eb25f3fa52a724c524f87943aa4feaf5179877..a34cf23790d5bf57abdfa2c1ecaa64cc5d1ee8be 100644
--- a/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 applyWaterFog(vec4 color)
 {
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterV.glsl b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
index 608a7a580779e47b9c4957ac8035b64625aabee5..161c794c68f7883207ae8227d43179e21b28ce8f 100644
--- a/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
diff --git a/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl b/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
index 5ac9e966014fe351708a64b3e9f6cb2742559b9a..6f821f893d6faab36e2d78c2ce054574aee3c3a6 100644
--- a/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl b/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
index c5f69c4ad4bc6340c635527e7728fbe0a26666c8..d1c98bf70cbfc39b54e6e28e96e266de3828a4a0 100644
--- a/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void main()
 {
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
index ad128dae8dd298629a594f749d4ff95aa15ada97..9c59e8c3ad719d3f9d298c849354098b0b1d619d 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl
index 1742b9fc1c313af691bb029e3294b30f4f1859a6..1fee99c446f214a9237d1225b86cd5906613a46f 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 
 uniform sampler2D diffuseMap;
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl
index 68b6603b4a794eb15a77ddc53cadc8f9cd5395c8..fb5da21c72227b8ce91e757fa23a39d6904d1a61 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 
 uniform sampler2D diffuseMap;
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..1bdaccf9b8f2598666b19a1780626949ea71cf2b
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl
@@ -0,0 +1,17 @@
+/** 
+ * @file lightFullbrightShinyWaterF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+ 
+#version 120
+
+uniform sampler2D diffuseMap;
+uniform samplerCube environmentMap;
+
+void fullbright_shiny_lighting_water() 
+{
+	gl_FragColor = texture2D(diffuseMap, gl_TexCoord[0].xy);
+}
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightWaterF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightWaterF.glsl
index 693ed289f2fa8acb93f776736e706d2195bb28b4..2e94d3bbf1955bea23c80c08691f7de8af140150 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightWaterF.glsl
@@ -5,6 +5,8 @@
  * $/LicenseInfo$
  */
 
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncSpecularV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncSpecularV.glsl
index b888e7032536a048619a26e5779e454d1dc1d8d7..714f9a2551005f78c2a88bdd869b8163299236f8 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncSpecularV.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncSpecularV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLight(vec3 n, vec3 l)
 {
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl
index 4b6d95e1774bcb372fee3963f1823952f480c8ab..65b45f80818c376fe9d8ca1fd16fb73a1189e327 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 
 float calcDirectionalLight(vec3 n, vec3 l)
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightShinyF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightShinyF.glsl
index b127b1f8eaeeba38be850a8fafdea38e89d8a163..7f65ea76f71c156fbbfc3119e3f791c69b782bfe 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightShinyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 
 uniform sampler2D diffuseMap;
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightShinyWaterF.glsl
index 05ad3256afe5cfdb9a191652a87282c6d55460cc..8f13e6dc0447138b2d4cf8afa8008b07e3a91e0f 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightShinyWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightShinyWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 
 uniform sampler2D diffuseMap;
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightSpecularV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightSpecularV.glsl
index b1a7cb46ffb2f4dda0722700d5aac2bb33b78d23..56f31f6a79b2f7c8ae0feb748178e31efd771caf 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightSpecularV.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightSpecularV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLight(vec3 n, vec3 l);
 
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
index f6afa6a3aef3b6d456049cbc719c0486740f043e..64d549ff5299d898a385ec6c6062227a65c38f38 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLight(vec3 n, vec3 l);
 
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightWaterF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightWaterF.glsl
index e5e6ddc2d882fb0c72d3647234511810f54b189f..c5d084c1323509ced287ea0bae5a2e4ce8f782da 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class1/lighting/sumLightsSpecularV.glsl b/indra/newview/app_settings/shaders/class1/lighting/sumLightsSpecularV.glsl
index a0649aea88bb2b727652ec8572bb96b9a7057ed3..732d2464719fa82dee191028f128d9668297f8bd 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/sumLightsSpecularV.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/sumLightsSpecularV.glsl
@@ -4,6 +4,9 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+  
+#version 120
+
 float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da);
 vec3 atmosAmbient(vec3 light);
 vec3 atmosAffectDirectionalLight(float lightIntensity);
diff --git a/indra/newview/app_settings/shaders/class1/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class1/lighting/sumLightsV.glsl
index c7d40d853f5825588f33e3a345c849c1738a93ae..73e1a1ec26eaa41c3b1adfbd625b23707e0fced5 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/sumLightsV.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/sumLightsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLight(vec3 n, vec3 l);
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightF.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightF.glsl
index 9da4c2c92b301f4f84fec73122ca6a0e0f0c6128..afc3dc89bf457484f050ff0771bedc3af5a202a7 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void fullbright_lighting();
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyF.glsl
index 1c8a9a1a3040527410dae0a27ad9a8cefd019fc0..3dc4294f6781d11bf7a86ead0232a3a2430b0451 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void fullbright_shiny_lighting();
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..f0baeeeee58c787e65ec53f65d4cd4ffe4fb0de1
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
@@ -0,0 +1,39 @@
+/** 
+ * @file shinySimpleSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	vec4 norm = gl_Vertex;
+	norm.xyz += gl_Normal.xyz;
+	norm.xyz = (mat*norm).xyz;
+	norm.xyz = normalize(norm.xyz-pos.xyz);
+		
+	vec3 ref = reflect(pos.xyz, -norm.xyz);
+
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0);
+
+	calcAtmospherics(pos.xyz);
+
+	gl_FrontColor = gl_Color;
+	
+	gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+	
+	gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl
index 032def63b302b1dca6f1e12a381001c28aa20d19..02367b94393739692337d0740827c8246924ee80 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..5daf66fb3108400b3613858b3c2f2139f14f0acd
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl
@@ -0,0 +1,15 @@
+/** 
+ * @file fullbrightShinyWaterF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+ 
+#version 120
+
+void fullbright_shiny_lighting_water();
+
+void main() 
+{
+	fullbright_shiny_lighting_water();
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..02ff3cc2a96382d969bc020b3f87604cc9e462e7
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl
@@ -0,0 +1,37 @@
+/** 
+ * @file fullbrightSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+	//transform vertex
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	vec4 norm = gl_Vertex;
+	norm.xyz += gl_Normal.xyz;
+	norm.xyz = (mat*norm).xyz;
+	norm.xyz = normalize(norm.xyz-pos.xyz);
+		
+	calcAtmospherics(pos.xyz);
+
+	gl_FrontColor = gl_Color;
+	
+	gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+		
+	gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl
index 914e417ca0baaa6e2afe88bb6102e4202ab32535..38e07dbd80c545f3413f3ca3c5913ebd9a6bc040 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightWaterF.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightWaterF.glsl
index df76e9e1eb7f2b63d3dbcb7217f68c1e2fce138a..afaac4f69c524555118bb5029a45e8dcf914161e 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void fullbright_lighting_water();
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl
index 6bcd44506d72b89ff8179f519c7a7b231ba961ac..2cf7a69baac7ae157088045d51fb6fe19bc886d1 100644
--- a/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void shiny_lighting();
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..4146646058de4abbfb34a057243967421f838f7b
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl
@@ -0,0 +1,39 @@
+/** 
+ * @file shinySimpleSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	vec4 norm = gl_Vertex;
+	norm.xyz += gl_Normal.xyz;
+	norm.xyz = (mat*norm).xyz;
+	norm.xyz = normalize(norm.xyz-pos.xyz);
+		
+	vec3 ref = reflect(pos.xyz, -norm.xyz);
+
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0);
+
+	calcAtmospherics(pos.xyz);
+
+	vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.));
+	gl_FrontColor = color;
+	
+	gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
index 074892c98e72043be0561f91338dac770029ff2e..6ea83b721defd91a9cf7cf46d665f5eed8ce517a 100644
--- a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
@@ -12,7 +14,7 @@ uniform vec4 origin;
 void main()
 {
 	//transform vertex
-	gl_Position = ftransform(); //gl_ModelViewProjectionMatrix * gl_Vertex;
+	gl_Position = ftransform();
 	
 	vec4 pos = (gl_ModelViewMatrix * gl_Vertex);
 	vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyWaterF.glsl
index 54b30573e7e355a168f2beed65526a22888a53be..e3babe2210f6c10fcb7f5347448893d409cefb16 100644
--- a/indra/newview/app_settings/shaders/class1/objects/shinyWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/shinyWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void shiny_lighting_water();
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
index 61c2ce42720a8a56f253400eb267d156acd7b2a6..d449d37c0c7f499b1ccce65b25d0d39af2c20060 100644
--- a/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void default_lighting();
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..be38a14d527c7eb5d588a6d3780e3dd3b4e14aab
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl
@@ -0,0 +1,39 @@
+/** 
+ * @file simpleSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+	//transform vertex
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	vec4 norm = gl_Vertex;
+	norm.xyz += gl_Normal.xyz;
+	norm.xyz = (mat*norm).xyz;
+	norm.xyz = normalize(norm.xyz-pos.xyz);
+		
+	calcAtmospherics(pos.xyz);
+
+	vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.));
+	gl_FrontColor = color;
+	
+	gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+	
+	gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
index ced1a4be01df1940fdb1c7bc889e9611fbbd0425..0d8e14e2e38ee1f3eafd858b2db37a208680c227 100644
--- a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 void calcAtmospherics(vec3 inPositionEye);
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleWaterF.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleWaterF.glsl
index 5e44212aedf7e1cd7f6b13e220e50de8a48623dd..68bd81e029328495ea7350222507de43257b9fdc 100644
--- a/indra/newview/app_settings/shaders/class1/objects/simpleWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void default_lighting_water();
 
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsF.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsF.glsl
index 7a05b8c8c64babcf029064530df4933f0c1079a8..f337bde32922bf4b183ec7a8288f10830c0f4d3a 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsF.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec3 atmosLighting(vec3 light)
 {
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsHelpersV.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsHelpersV.glsl
index 874f2b48432418b6ecf7b7348e9b8304f4a73914..4b402a70287c6fe1580983b0d39a599fbe783346 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsHelpersV.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsHelpersV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec3 atmosAmbient(vec3 light)
 {
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsV.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsV.glsl
index 7ead9ddf26622cce81018ce9f4cef34965ee6665..20948b1e468d07d6a6bd402c09cdf9a5623cde59 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsV.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void setPositionEye(vec3 v);
 
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsF.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsF.glsl
index f6032f8d4165ed1b1bf8c947240fce7d8cc092f8..8a2c2a71862fb7402c313f95af0e03248262e8c4 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsF.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_PositionEye;
 
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsV.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsV.glsl
index a696ddf60797e3a78bb798eeee39b7befdd19b79..a1dd4ed5fedefd11993c77b5fa129a1815093d98 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsV.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsVarsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_PositionEye;
 
diff --git a/indra/newview/app_settings/shaders/class1/windlight/gammaF.glsl b/indra/newview/app_settings/shaders/class1/windlight/gammaF.glsl
index 4a1899798a2f9721c882308d2a680ccdca02cd89..7aed1fd3b5cd52c82194cc5b751d832e9259bb99 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/gammaF.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/gammaF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec4 gamma;
 
diff --git a/indra/newview/app_settings/shaders/class1/windlight/transportF.glsl b/indra/newview/app_settings/shaders/class1/windlight/transportF.glsl
index b78b90545e17bc87b001bd841baa8ce271f42634..6780dc4d3e1249c3eca244acc7e5fe4ced295851 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/transportF.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/transportF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec3 atmosTransport(vec3 light)
 {
diff --git a/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl b/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
index 47300f0b39ab9f8a6cc8c8d6a3115b7a09697005..172c2ca078ad4a7f5402a8b882d4f18877c23013 100644
--- a/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
+++ b/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec4 baseCol);
 void calcAtmospherics(vec3 inPositionEye);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
index e2d7cd94da0786b56f9a3ac5815298d2710efcba..6dfc1b952c1fc52bb82e8d690228c57d65d13457 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
@@ -12,7 +14,6 @@ uniform sampler2DRectShadow shadowMap0;
 uniform sampler2DRectShadow shadowMap1;
 uniform sampler2DRectShadow shadowMap2;
 uniform sampler2DRectShadow shadowMap3;
-uniform sampler2D noiseMap;
 uniform sampler2DRect depthMap;
 
 uniform mat4 shadow_matrix[6];
@@ -27,7 +28,7 @@ varying vec3 vary_ambient;
 varying vec3 vary_directional;
 varying vec3 vary_fragcoord;
 varying vec3 vary_position;
-varying vec3 vary_light;
+varying vec3 vary_pointlight_col;
 
 uniform float shadow_bias;
 
@@ -68,8 +69,6 @@ void main()
 	vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5;
 	frag *= screen_res;
 	
-	vec3 samp_pos = getPosition(frag).xyz;
-	
 	float shadow = 1.0;
 	vec4 pos = vec4(vary_position, 1.0);
 	
@@ -106,16 +105,21 @@ void main()
 		}
 	}
 	
+	vec4 diff= texture2D(diffuseMap, gl_TexCoord[0].xy);
+
 	vec4 col = vec4(vary_ambient + vary_directional.rgb*shadow, gl_Color.a);
-	vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy) * col;
+	vec4 color = diff * col;
 	
 	color.rgb = atmosLighting(color.rgb);
 
 	color.rgb = scaleSoftClip(color.rgb);
 
+	color.rgb += diff.rgb * vary_pointlight_col.rgb;
+
 	//gl_FragColor = gl_Color;
 	gl_FragColor = color;
-	//gl_FragColor = vec4(1,0,1,1)*shadow;
+	//gl_FragColor.r = 0.0;
+	//gl_FragColor = vec4(1,shadow,1,1);
 	
 }
 
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..d227346163e052e600adeb27bf51434921bc82fd
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
@@ -0,0 +1,110 @@
+/** 
+ * @file alphaSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+ 
+#version 120
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void calcAtmospherics(vec3 inPositionEye);
+
+float calcDirectionalLight(vec3 n, vec3 l);
+mat4 getObjectSkinnedTransform();
+vec3 atmosAmbient(vec3 light);
+vec3 atmosAffectDirectionalLight(float lightIntensity);
+vec3 scaleDownLight(vec3 light);
+vec3 scaleUpLight(vec3 light);
+
+varying vec3 vary_ambient;
+varying vec3 vary_directional;
+varying vec3 vary_fragcoord;
+varying vec3 vary_position;
+varying vec3 vary_pointlight_col;
+
+uniform float near_clip;
+uniform float shadow_offset;
+uniform float shadow_bias;
+
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight)
+{
+	//get light vector
+	vec3 lv = lp.xyz-v;
+	
+	//get distance
+	float d = length(lv);
+	
+	//normalize light vector
+	lv *= 1.0/d;
+	
+	//distance attenuation
+	float dist2 = d*d/(la*la);
+	float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+
+	// spotlight coefficient.
+	float spot = max(dot(-ln, lv), is_pointlight);
+	da *= spot*spot; // GL_SPOT_EXPONENT=2
+
+	//angular attenuation
+	da *= calcDirectionalLight(n, lv);
+
+	return da;	
+}
+
+void main()
+{
+	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+	
+	mat4 mat = getObjectSkinnedTransform();
+	
+	mat = gl_ModelViewMatrix * mat;
+	
+	vec3 pos = (mat*gl_Vertex).xyz;
+	
+	gl_Position = gl_ProjectionMatrix * vec4(pos, 1.0);
+	
+	vec4 n = gl_Vertex;
+	n.xyz += gl_Normal.xyz;
+	n.xyz = (mat*n).xyz;
+	n.xyz = normalize(n.xyz-pos.xyz);
+	
+	vec3 norm = n.xyz;
+	
+	float dp_directional_light = max(0.0, dot(norm, gl_LightSource[0].position.xyz));
+	vary_position = pos.xyz + gl_LightSource[0].position.xyz * (1.0-dp_directional_light)*shadow_offset;
+			
+	calcAtmospherics(pos.xyz);
+
+	//vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.));
+	vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
+
+	// Collect normal lights
+	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a);
+	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a);
+	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a);
+	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a);
+	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a);
+	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a);
+	
+	vary_pointlight_col = col.rgb*gl_Color.rgb;
+
+	col.rgb = vec3(0,0,0);
+
+	// Add windlight lights
+	col.rgb = atmosAmbient(vec3(0.));
+		
+	vary_ambient = col.rgb*gl_Color.rgb;
+	vary_directional.rgb = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
+	
+	col.rgb = min(col.rgb*gl_Color.rgb, 1.0);
+	
+	gl_FrontColor = col;
+
+	gl_FogFragCoord = pos.z;
+	
+	pos.xyz = (gl_ModelViewProjectionMatrix * gl_Vertex).xyz;
+	vary_fragcoord.xyz = pos.xyz + vec3(0,0,near_clip);
+	
+}
+
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl
index 45f727951e38166e3ca8a4ce86ac67c2be833093..86f014df35f903b93b8eb89b37bcc63cd70f5020 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl
@@ -4,12 +4,13 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 void calcAtmospherics(vec3 inPositionEye);
 
 float calcDirectionalLight(vec3 n, vec3 l);
-float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
 
 vec3 atmosAmbient(vec3 light);
 vec3 atmosAffectDirectionalLight(float lightIntensity);
@@ -20,12 +21,37 @@ varying vec3 vary_ambient;
 varying vec3 vary_directional;
 varying vec3 vary_fragcoord;
 varying vec3 vary_position;
-varying vec3 vary_light;
+varying vec3 vary_pointlight_col;
 
 uniform float near_clip;
 uniform float shadow_offset;
 uniform float shadow_bias;
 
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight)
+{
+	//get light vector
+	vec3 lv = lp.xyz-v;
+	
+	//get distance
+	float d = length(lv);
+	
+	//normalize light vector
+	lv *= 1.0/d;
+	
+	//distance attenuation
+	float dist2 = d*d/(la*la);
+	float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+
+	// spotlight coefficient.
+	float spot = max(dot(-ln, lv), is_pointlight);
+	da *= spot*spot; // GL_SPOT_EXPONENT=2
+
+	//angular attenuation
+	da *= calcDirectionalLight(n, lv);
+
+	return da;	
+}
+
 void main()
 {
 	//transform vertex
@@ -44,25 +70,25 @@ void main()
 	//vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.));
 	vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
 
-	// Collect normal lights (need to be divided by two, as we later multiply by 2)
-	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a);
-	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a);
-	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a);
-	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a);
-	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a);
-	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a);
-	col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
-	col.rgb = scaleDownLight(col.rgb);
+	// Collect normal lights
+	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a);
+	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a);
+	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a);
+	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a);
+	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a);
+	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a);
 	
+	vary_pointlight_col = col.rgb*gl_Color.rgb;
+
+	col.rgb = vec3(0,0,0);
+
 	// Add windlight lights
-	col.rgb += atmosAmbient(vec3(0.));
-	
-	vary_light = gl_LightSource[0].position.xyz;
+	col.rgb = atmosAmbient(vec3(0.));
 	
 	vary_ambient = col.rgb*gl_Color.rgb;
 	vary_directional.rgb = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
 	
-	col.rgb = min(col.rgb*gl_Color.rgb, 1.0);
+	col.rgb = col.rgb*gl_Color.rgb;
 	
 	gl_FrontColor = col;
 
diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaF.glsl
deleted file mode 100644
index 5ecbbd2c4f787f981b5120bcd64ede58a1d2cc04..0000000000000000000000000000000000000000
--- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaF.glsl
+++ /dev/null
@@ -1,98 +0,0 @@
-/** 
- * @file avatarAlphaF.glsl
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * $/LicenseInfo$
- */
-
-#extension GL_ARB_texture_rectangle : enable
-
-uniform sampler2D diffuseMap;
-uniform sampler2DRectShadow shadowMap0;
-uniform sampler2DRectShadow shadowMap1;
-uniform sampler2DRectShadow shadowMap2;
-uniform sampler2DRectShadow shadowMap3;
-uniform sampler2D noiseMap;
-
-uniform mat4 shadow_matrix[6];
-uniform vec4 shadow_clip;
-uniform vec2 screen_res;
-uniform vec2 shadow_res;
-
-vec3 atmosLighting(vec3 light);
-vec3 scaleSoftClip(vec3 light);
-
-varying vec3 vary_ambient;
-varying vec3 vary_directional;
-varying vec3 vary_position;
-varying vec3 vary_normal;
-
-uniform float shadow_bias;
-
-float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
-{
-	stc.xyz /= stc.w;
-	stc.z += shadow_bias;
-	
-	float cs = shadow2DRect(shadowMap, stc.xyz).x;
-	float shadow = cs;
-
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
-			
-	return shadow/5.0;
-}
-
-void main() 
-{
-	float shadow = 1.0;
-	vec4 pos = vec4(vary_position, 1.0);
-	vec3 norm = normalize(vary_normal);
-	
-	//vec3 nz = texture2D(noiseMap, gl_FragCoord.xy/128.0).xyz;
-
-	vec4 spos = pos;
-	
-	if (spos.z > -shadow_clip.w)
-	{	
-		vec4 lpos;
-		
-		if (spos.z < -shadow_clip.z)
-		{
-			lpos = shadow_matrix[3]*spos;
-			lpos.xy *= shadow_res;
-			shadow = pcfShadow(shadowMap3, lpos, 1.5);
-			shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
-		}
-		else if (spos.z < -shadow_clip.y)
-		{
-			lpos = shadow_matrix[2]*spos;
-			lpos.xy *= shadow_res;
-			shadow = pcfShadow(shadowMap2, lpos, 1.5);
-		}
-		else if (spos.z < -shadow_clip.x)
-		{
-			lpos = shadow_matrix[1]*spos;
-			lpos.xy *= shadow_res;
-			shadow = pcfShadow(shadowMap1, lpos, 1.5);
-		}
-		else
-		{
-			lpos = shadow_matrix[0]*spos;
-			lpos.xy *= shadow_res;
-			shadow = pcfShadow(shadowMap0, lpos, 1.5);
-		}
-	}
-	
-	
-	vec4 col = vec4(vary_ambient + vary_directional*shadow, gl_Color.a);	
-	vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy) * col;
-	
-	color.rgb = atmosLighting(color.rgb);
-
-	color.rgb = scaleSoftClip(color.rgb);
-
-	gl_FragColor = color;
-}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl
index d7d1111ba8415196bb987716c6259bfd0a94b30f..495e86c8db084f43eace6d130f07536a127fde9a 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 mat4 getSkinnedTransform();
@@ -20,12 +22,38 @@ vec3 scaleUpLight(vec3 light);
 varying vec3 vary_position;
 varying vec3 vary_ambient;
 varying vec3 vary_directional;
-varying vec3 vary_normal;
+varying vec3 vary_fragcoord;
+varying vec3 vary_pointlight_col;
 
 uniform float near_clip;
 uniform float shadow_offset;
 uniform float shadow_bias;
 
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight)
+{
+	//get light vector
+	vec3 lv = lp.xyz-v;
+	
+	//get distance
+	float d = length(lv);
+	
+	//normalize light vector
+	lv *= 1.0/d;
+	
+	//distance attenuation
+	float dist2 = d*d/(la*la);
+	float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+
+	// spotlight coefficient.
+	float spot = max(dot(-ln, lv), is_pointlight);
+	da *= spot*spot; // GL_SPOT_EXPONENT=2
+
+	//angular attenuation
+	da *= calcDirectionalLight(n, lv);
+
+	return da;	
+}
+
 void main()
 {
 	gl_TexCoord[0] = gl_MultiTexCoord0;
@@ -48,7 +76,6 @@ void main()
 	
 	float dp_directional_light = max(0.0, dot(norm, gl_LightSource[0].position.xyz));
 	vary_position = pos.xyz + gl_LightSource[0].position.xyz * (1.0-dp_directional_light)*shadow_offset;
-	vary_normal = norm;	
 	
 	calcAtmospherics(pos.xyz);
 
@@ -56,18 +83,20 @@ void main()
 
 	vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
 
-	// Collect normal lights (need to be divided by two, as we later multiply by 2)
-	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a);
-	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a);
-	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a);
-	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a);
-	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a);
-	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a);
-	col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
-	col.rgb = scaleDownLight(col.rgb);
+	// Collect normal lights
+	col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a);
+	col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a);
+	col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a);
+	col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a);
+	col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a);
+	col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a);
 	
+	vary_pointlight_col = col.rgb*gl_Color.rgb;
+
+	col.rgb = vec3(0,0,0);
+
 	// Add windlight lights
-	col.rgb += atmosAmbient(vec3(0.));
+	col.rgb = atmosAmbient(vec3(0.));
 	
 	vary_ambient = col.rgb*gl_Color.rgb;
 	vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
@@ -77,7 +106,7 @@ void main()
 	gl_FrontColor = col;
 
 	gl_FogFragCoord = pos.z;
-
+	vary_fragcoord.xyz = pos.xyz + vec3(0,0,near_clip);
 }
 
 
diff --git a/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl
deleted file mode 100644
index 258a9b7c400b078870f959b32de85be3b95bcf72..0000000000000000000000000000000000000000
--- a/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl
+++ /dev/null
@@ -1,81 +0,0 @@
-/** 
- * @file blurLightF.glsl
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * $/LicenseInfo$
- */
-
-#extension GL_ARB_texture_rectangle : enable
-
-uniform sampler2DRect depthMap;
-uniform sampler2DRect normalMap;
-uniform sampler2DRect lightMap;
-
-uniform float dist_factor;
-uniform float blur_size;
-uniform vec2 delta;
-uniform vec3 kern[4];
-uniform float kern_scale;
-
-varying vec2 vary_fragcoord;
-
-uniform mat4 inv_proj;
-uniform vec2 screen_res;
-
-vec4 getPosition(vec2 pos_screen)
-{
-	float depth = texture2DRect(depthMap, pos_screen.xy).a;
-	vec2 sc = pos_screen.xy*2.0;
-	sc /= screen_res;
-	sc -= vec2(1.0,1.0);
-	vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
-	vec4 pos = inv_proj * ndc;
-	pos /= pos.w;
-	pos.w = 1.0;
-	return pos;
-}
-
-void main() 
-{
-	vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz;
-	norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
-	vec3 pos = getPosition(vary_fragcoord.xy).xyz;
-	vec4 ccol = texture2DRect(lightMap, vary_fragcoord.xy).rgba;
-	
-	vec2 dlt = kern_scale * delta / (1.0+norm.xy*norm.xy);
-	
-	dlt /= max(-pos.z*dist_factor, 1.0);
-	
-	vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free'
-	vec4 col = defined_weight.xyxx * ccol;
-	
-	for (int i = 1; i < 4; i++)
-	{
-		vec2 tc = vary_fragcoord.xy + kern[i].z*dlt;
-	        vec3 samppos = getPosition(tc).xyz; 
-		float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
-		if (d*d <= 0.003)
-		{
-			col += texture2DRect(lightMap, tc)*kern[i].xyxx;
-			defined_weight += kern[i].xy;
-		}
-	}
-	for (int i = 1; i < 4; i++)
-	{
-		vec2 tc = vary_fragcoord.xy - kern[i].z*dlt;
-	        vec3 samppos = getPosition(tc).xyz; 
-		float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
-		if (d*d <= 0.003)
-		{
-			col += texture2DRect(lightMap, tc)*kern[i].xyxx;
-			defined_weight += kern[i].xy;
-		}
-	}
-
-
-
-	col /= defined_weight.xyxx;
-	
-	gl_FragColor = col;
-}
-
diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl
index ff32a15c54633d6fd442c2266e490f8a707e0408..3155f3f9295a3602e8eaa1915910fd58226a48d8 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl
@@ -4,14 +4,14 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
 uniform sampler2DRect depthMap;
 uniform sampler2DRect normalMap;
 
-uniform float gi_dist_cutoff;
-
 varying vec2 vary_fragcoord;
 
 uniform float depth_cutoff;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeV.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeV.glsl
index 74f2bd9818954e9797a4e99d678e7e68cbe08a48..b3413c301fc442633173347773d2f1c761d489ad 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/edgeV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/edgeV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class2/deferred/postDeferredF.glsl
deleted file mode 100644
index 757e3e7aabcd375687f8531f23f03f98f7e8eb61..0000000000000000000000000000000000000000
--- a/indra/newview/app_settings/shaders/class2/deferred/postDeferredF.glsl
+++ /dev/null
@@ -1,59 +0,0 @@
-/** 
- * @file postDeferredF.glsl
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * $/LicenseInfo$
- */
-
-uniform sampler2DRect diffuseRect;
-uniform sampler2DRect localLightMap;
-uniform sampler2DRect sunLightMap;
-uniform sampler2DRect giLightMap;
-uniform sampler2D	  luminanceMap;
-uniform sampler2DRect lightMap;
-
-uniform vec3 gi_lum_quad;
-uniform vec3 sun_lum_quad;
-uniform vec3 lum_quad;
-uniform float lum_lod;
-uniform vec4 ambient;
-
-uniform vec3 gi_quad;
-
-uniform vec2 screen_res;
-varying vec2 vary_fragcoord;
-
-void main() 
-{
-	vec2 tc = vary_fragcoord.xy;
-	vec3 lcol = texture2DLod(luminanceMap, tc/screen_res, lum_lod).rgb;
-
-	float lum = sqrt(lcol.r)*lum_quad.x+lcol.r*lcol.r*lum_quad.y+lcol.r*lum_quad.z;
-	
-	vec4 diff = texture2DRect(diffuseRect, vary_fragcoord.xy);
-
-	float ambocc = texture2DRect(lightMap, vary_fragcoord.xy).g;
-			
-	vec3 gi_col = texture2DRect(giLightMap, vary_fragcoord.xy).rgb;
-	gi_col = gi_col*gi_col*gi_quad.x + gi_col*gi_quad.y+gi_quad.z*ambocc*ambient.rgb;
-	gi_col *= diff;
-	
-	vec4 sun_col =	texture2DRect(sunLightMap, vary_fragcoord.xy);
-	
-	vec3 local_col = texture2DRect(localLightMap, vary_fragcoord.xy).rgb;
-		
-
-	float sun_lum = 1.0-lum;
-	sun_lum = sun_lum*sun_lum*sun_lum_quad.x + sun_lum*sun_lum_quad.y+sun_lum_quad.z;
-		
-	float gi_lum = lum;
-	gi_lum = gi_lum*gi_lum*gi_lum_quad.x+gi_lum*gi_lum_quad.y+gi_lum_quad.z;
-	gi_col *= 1.0/gi_lum;
-		
-	vec3 col = sun_col.rgb*(1.0+max(sun_lum,0.0))+gi_col+local_col;
-	
-	gl_FragColor.rgb = col.rgb;
-	gl_FragColor.a = max(sun_lum*min(sun_col.r+sun_col.g+sun_col.b, 1.0), sun_col.a);
-	
-	//gl_FragColor.rgb = texture2DRect(giLightMap, vary_fragcoord.xy).rgb;
-}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/postDeferredV.glsl b/indra/newview/app_settings/shaders/class2/deferred/postDeferredV.glsl
deleted file mode 100644
index 0ec81dcb02ed61b19b8b93d6dd0356a00171a584..0000000000000000000000000000000000000000
--- a/indra/newview/app_settings/shaders/class2/deferred/postDeferredV.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-/** 
- * @file postDeferredV.glsl
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * $/LicenseInfo$
- */
-
-varying vec2 vary_fragcoord;
-uniform vec2 screen_res;
-
-void main()
-{
-	//transform vertex
-	gl_Position = ftransform(); 
-	vec4 pos = gl_ModelViewProjectionMatrix * gl_Vertex;
-	vary_fragcoord = (pos.xy*0.5+0.5)*screen_res;
-}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
index 1067be1e6ed326ff0d069a39ff83b607afefb014..0160e84278fc665f06adde57cda481a1b9d39f52 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightV.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightV.glsl
index 9d187b46e208ba0b70005c661fc342ec013bd271..8f0bcca76b86a22f32101d0c81b3631110fff0c9 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/softenLightV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec2 screen_res;
 
diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl
index d0e242c2d434e9f833ce995ec045998d4a33f2e4..50b9ef276e6ab7bb98a51eb5a00dff4ce9afa0d7 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl
@@ -4,7 +4,7 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
-
+ 
 #version 120
 
 #extension GL_ARB_texture_rectangle : enable
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
index f565d3bdb996e6538b11a8bdca9cddce1dc7a74e..4369b3b34ff8da6af4aabcf4b57e58b02024dd2b 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
@@ -17,9 +19,6 @@ uniform sampler2DRectShadow shadowMap2;
 uniform sampler2DRectShadow shadowMap3;
 uniform sampler2DShadow shadowMap4;
 uniform sampler2DShadow shadowMap5;
-uniform sampler2D noiseMap;
-
-uniform sampler2D		lightFunc;
 
 
 // Inputs
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
index 4e33a1af450b84a12097dd54695c69da4bfa6128..847b36b1ace5722cf0a4cdec723bc75b2549c177 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
@@ -4,6 +4,8 @@
  * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
  * $License$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
@@ -19,8 +21,6 @@ uniform sampler2DShadow shadowMap4;
 uniform sampler2DShadow shadowMap5;
 uniform sampler2D noiseMap;
 
-uniform sampler2D		lightFunc;
-
 // Inputs
 uniform mat4 shadow_matrix[6];
 uniform vec4 shadow_clip;
@@ -60,58 +60,50 @@ vec4 getPosition(vec2 pos_screen)
 float calcAmbientOcclusion(vec4 pos, vec3 norm)
 {
 	float ret = 1.0;
-	
-	float dist = dot(pos.xyz,pos.xyz);
-	
-	if (dist < 64.0*64.0)
-	{
-		vec2 kern[8];
-		// exponentially (^2) distant occlusion samples spread around origin
-		kern[0] = vec2(-1.0, 0.0) * 0.125*0.125;
-		kern[1] = vec2(1.0, 0.0) * 0.250*0.250;
-		kern[2] = vec2(0.0, 1.0) * 0.375*0.375;
-		kern[3] = vec2(0.0, -1.0) * 0.500*0.500;
-		kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625;
-		kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
-		kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
-		kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
 
-		vec2 pos_screen = vary_fragcoord.xy;
-		vec3 pos_world = pos.xyz;
-		vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy;
+	vec2 kern[8];
+	// exponentially (^2) distant occlusion samples spread around origin
+	kern[0] = vec2(-1.0, 0.0) * 0.125*0.125;
+	kern[1] = vec2(1.0, 0.0) * 0.250*0.250;
+	kern[2] = vec2(0.0, 1.0) * 0.375*0.375;
+	kern[3] = vec2(0.0, -1.0) * 0.500*0.500;
+	kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625;
+	kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
+	kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
+	kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
+
+	vec2 pos_screen = vary_fragcoord.xy;
+	vec3 pos_world = pos.xyz;
+	vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy;
 		
-		float angle_hidden = 0.0;
-		int points = 0;
+	float angle_hidden = 0.0;
+	int points = 0;
 		
-		float scale = min(ssao_radius / -pos_world.z, ssao_max_radius);
+	float scale = min(ssao_radius / -pos_world.z, ssao_max_radius);
 		
-		// it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?)
-		for (int i = 0; i < 8; i++)
-		{
-			vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect);
-			vec3 samppos_world = getPosition(samppos_screen).xyz; 
+	// it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?)
+	for (int i = 0; i < 8; i++)
+	{
+		vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect);
+		vec3 samppos_world = getPosition(samppos_screen).xyz; 
 			
-			vec3 diff = pos_world - samppos_world;
-			float dist2 = dot(diff, diff);
+		vec3 diff = pos_world - samppos_world;
+		float dist2 = dot(diff, diff);
 			
-			// assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area
-			// --> solid angle shrinking by the square of distance
-			//radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2
-			//(k should vary inversely with # of samples, but this is taken care of later)
+		// assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area
+		// --> solid angle shrinking by the square of distance
+		//radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2
+		//(k should vary inversely with # of samples, but this is taken care of later)
 			
-			//if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0)  // -0.05*norm to shift sample point back slightly for flat surfaces
-			//	angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional.  max of 1.0 (= ssao_factor_inv * ssao_factor)
-			angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv);
+		angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv);
 			
-			// 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" 
-			points = points + int(diff.z > -1.0);
-		}
+		// 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" 
+		points = points + int(diff.z > -1.0);
+	}
 		
-		angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0);
+	angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0);
 		
-		ret = (1.0 - (float(points != 0) * angle_hidden));
-		ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0);
-	}
+	ret = (1.0 - (float(points != 0) * angle_hidden));
 	
 	return min(ret, 1.0);
 }
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightV.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightV.glsl
index 9d092d9cea4cd340896a0fc0aa7c0e3642db4060..9beb513ad8a63a129b231381cb010e69c58b9510 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec4 vary_light;
 varying vec2 vary_fragcoord;
diff --git a/indra/newview/app_settings/shaders/class2/effects/blurF.glsl b/indra/newview/app_settings/shaders/class2/effects/blurF.glsl
index 4173709298730e933f1ed96a5d743f50efa3854d..a4ad0bfa15f1053bc0b8292cfb9c8eb26644ac0a 100644
--- a/indra/newview/app_settings/shaders/class2/effects/blurF.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/blurF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect RenderTexture;
 uniform float bloomStrength;
diff --git a/indra/newview/app_settings/shaders/class2/effects/blurV.glsl b/indra/newview/app_settings/shaders/class2/effects/blurV.glsl
index f66609527dbefc1c5d8e6428fcb5dd3b652c4924..d471a6c5e59582d536ad1d591462f7c683f3ad11 100644
--- a/indra/newview/app_settings/shaders/class2/effects/blurV.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/blurV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec2 texelSize;
 uniform vec2 blurDirection;
diff --git a/indra/newview/app_settings/shaders/class2/effects/colorFilterF.glsl b/indra/newview/app_settings/shaders/class2/effects/colorFilterF.glsl
index df41dae7575a58c0174696e5470649eda38dbdaf..66880b958e0c9b1d9e12f59a06b4225ab0d65e8c 100644
--- a/indra/newview/app_settings/shaders/class2/effects/colorFilterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/colorFilterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect RenderTexture;
 uniform float brightness;
diff --git a/indra/newview/app_settings/shaders/class2/effects/drawQuadV.glsl b/indra/newview/app_settings/shaders/class2/effects/drawQuadV.glsl
index e836caf93f527780863fa2673224597b73386d87..c35c500d62374029b3123963f81185bf44253c10 100644
--- a/indra/newview/app_settings/shaders/class2/effects/drawQuadV.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/drawQuadV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void main(void)
 {
diff --git a/indra/newview/app_settings/shaders/class2/effects/extractF.glsl b/indra/newview/app_settings/shaders/class2/effects/extractF.glsl
index 06d5fc979710059afa3229ade877de5df45d544b..e77baa5bee115ea038138883aacb1c546ddea94a 100644
--- a/indra/newview/app_settings/shaders/class2/effects/extractF.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/extractF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect RenderTexture;
 uniform float extractLow;
diff --git a/indra/newview/app_settings/shaders/class2/effects/nightVisionF.glsl b/indra/newview/app_settings/shaders/class2/effects/nightVisionF.glsl
index 0a2767ad02279873d82413f6ad95e46476e72f6f..8e0eec6f5e9a98c5727f73aa929832db345489d5 100644
--- a/indra/newview/app_settings/shaders/class2/effects/nightVisionF.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/nightVisionF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect RenderTexture;
 uniform sampler2D NoiseTexture;
diff --git a/indra/newview/app_settings/shaders/class2/effects/simpleF.glsl b/indra/newview/app_settings/shaders/class2/effects/simpleF.glsl
index 29ad9a995b961ba972640106d7f7d4c00c6ca506..98a50e22fc441d72aff3a2e87a77470bc1acc70b 100644
--- a/indra/newview/app_settings/shaders/class2/effects/simpleF.glsl
+++ b/indra/newview/app_settings/shaders/class2/effects/simpleF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect RenderTexture;
 
diff --git a/indra/newview/app_settings/shaders/class2/environment/terrainF.glsl b/indra/newview/app_settings/shaders/class2/environment/terrainF.glsl
index 32259acf1b5e82c1f1e9d358ff7c2a422378c888..bbb8951f3a3e9d4cab7c98637195735f196421a3 100644
--- a/indra/newview/app_settings/shaders/class2/environment/terrainF.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/terrainF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D detail_0;
 uniform sampler2D detail_1;
diff --git a/indra/newview/app_settings/shaders/class2/environment/terrainV.glsl b/indra/newview/app_settings/shaders/class2/environment/terrainV.glsl
index 2234f0bd89095d62d0042ce04a0ceeba8a377965..84906c16bffd638afc6024c84d4c1bc2c43299c2 100644
--- a/indra/newview/app_settings/shaders/class2/environment/terrainV.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/terrainV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 void calcAtmospherics(vec3 inPositionEye);
 
diff --git a/indra/newview/app_settings/shaders/class2/environment/terrainWaterF.glsl b/indra/newview/app_settings/shaders/class2/environment/terrainWaterF.glsl
index 1650912fc8f33f4d86c1516620e09fe19b5832a7..7590c542ef46cea4a5f3d901ab7809c4b5ebbd4d 100644
--- a/indra/newview/app_settings/shaders/class2/environment/terrainWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/terrainWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D detail_0;
 uniform sampler2D detail_1;
diff --git a/indra/newview/app_settings/shaders/class2/environment/underWaterF.glsl b/indra/newview/app_settings/shaders/class2/environment/underWaterF.glsl
index 9e936a37901b51607f640431b0c9584f9553d9d8..900f1a6cb83668b0ae79622e49861bfd969ae4e0 100644
--- a/indra/newview/app_settings/shaders/class2/environment/underWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/underWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform sampler2D bumpMap;   
diff --git a/indra/newview/app_settings/shaders/class2/environment/waterF.glsl b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
index e477107c0bf6c0f163262d75dc147eff9122f374..f4f6b6e90fa0bd80dd494a0482e14b25c7beffc8 100644
--- a/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec3 scaleSoftClip(vec3 inColor);
 vec3 atmosTransport(vec3 inColor);
diff --git a/indra/newview/app_settings/shaders/class2/environment/waterFogF.glsl b/indra/newview/app_settings/shaders/class2/environment/waterFogF.glsl
index 7bcdcf5d5b6aaab3becf92fec1c7eacba5153d0a..9f3328cbf0cfe9bb6d019fe20edb90fd899ee91b 100644
--- a/indra/newview/app_settings/shaders/class2/environment/waterFogF.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/waterFogF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec4 lightnorm;
 uniform vec4 waterPlane;
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
index 269d11a085ae8665e8526981c15cfc7289fcbb7e..342bc2ab6673aaea1d76832502645140401d874e 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightF.glsl
index 9ffe3c6f4a733e1eeb5967531d585d48332ac2ef..dad18b588383139379889a453e207972cc1be2e2 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyF.glsl
index b7181dec3accd4f47df90bfa75a322905ef6e61f..73ff81e03a6ffdb2865356b48efde11a54136ae4 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform samplerCube environmentMap;
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..9b4b58436990b10e00a82b031fbcdbc0c061e7f1
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl
@@ -0,0 +1,31 @@
+/** 
+ * @file lightFullbrightShinyWaterF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+ 
+#version 120
+
+uniform sampler2D diffuseMap;
+uniform samplerCube environmentMap;
+
+vec3 fullbrightShinyAtmosTransport(vec3 light);
+vec3 fullbrightScaleSoftClip(vec3 light);
+vec4 applyWaterFog(vec4 color);
+
+void fullbright_shiny_lighting_water()
+{
+	vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy);
+	color.rgb *= gl_Color.rgb;
+	
+	vec3 envColor = textureCube(environmentMap, gl_TexCoord[1].xyz).rgb;	
+	color.rgb = mix(color.rgb, envColor.rgb, gl_Color.a);
+
+	color.rgb = fullbrightShinyAtmosTransport(color.rgb);
+	color.rgb = fullbrightScaleSoftClip(color.rgb);
+	color.a = max(color.a, gl_Color.a);
+
+	gl_FragColor = applyWaterFog(color);
+}
+
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightWaterF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightWaterF.glsl
index ee38790cc4363f71adcb9422b0dc365da5e9d80e..3d46c8d8744271a0a07e9ebfa2bc01a8a1f0bb5e 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightShinyF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightShinyF.glsl
index b96b5d75bcc4ce5df41f96017259e88311f864b7..ebe21320b43f8a8118623dae5bd7ba8f2fb5e9e9 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightShinyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 uniform samplerCube environmentMap;
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightShinyWaterF.glsl
index 0f5b2d6fcfe6afe4389d07e782b741ce5545c45c..7f48e2cf1d5ec1c5ed586b4101fb5b385545e451 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightShinyWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightShinyWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 
 uniform sampler2D diffuseMap;
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightSpecularV.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightSpecularV.glsl
index 6400b45d9e3acec89dffbf8cf7d4e73e76aaff8e..ad1dc4da77e17a34c9830f60566770f31cac6a64 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightSpecularV.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightSpecularV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 // All lights, no specular highlights
 
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
index 89ef510d7c7f793cbf728280d9f2b2638124a6da..a0f6e019ef12c4779a770c65553db3b44e2a0799 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 // All lights, no specular highlights
 
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightWaterF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightWaterF.glsl
index 016258bd181d8faff71d5fc12c62a7ec257d9907..97eba92d7b51f463f0e6cd877eb3ed6950855fad 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightWaterF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class2/lighting/sumLightsSpecularV.glsl b/indra/newview/app_settings/shaders/class2/lighting/sumLightsSpecularV.glsl
index 8cfeeb1cf9b9b51f08876bdf9191301e94c554f3..fde32ed0358f55c07f8124454c3086f775bb5cd8 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/sumLightsSpecularV.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/sumLightsSpecularV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da);
 vec3 calcPointLightSpecular(inout vec4 specular, vec3 view, vec3 v, vec3 n, vec3 l, float r, float pw, vec3 lightCol);
diff --git a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl
index a512b9d6fb3b2ac89b0ac70c5ee31b09b797f556..8fe49e3be026cebf056e9d6968361346abdd1465 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLight(vec3 n, vec3 l);
 float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
diff --git a/indra/newview/app_settings/shaders/class2/objects/shinyV.glsl b/indra/newview/app_settings/shaders/class2/objects/shinyV.glsl
index c428bbb28ef2bb4bf31227ee8e374e2f3b2ef27a..4cebb06df080e8b289cccaee534789d54d7177aa 100644
--- a/indra/newview/app_settings/shaders/class2/objects/shinyV.glsl
+++ b/indra/newview/app_settings/shaders/class2/objects/shinyV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 
diff --git a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsF.glsl b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsF.glsl
index 8baff24dbd1bdea46ddd14054053561fe47c222b..77d15fba9a938989469fcbbf151e0b5f76203f61 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsF.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 //////////////////////////////////////////////////////////
 // The fragment shader for the terrain atmospherics
diff --git a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsHelpersV.glsl b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsHelpersV.glsl
index 6883edc1f1bdb495255b354caac2c86e047bb363..8c5b864cbef69e49d0e0c71571ebcbcedbd0ac3c 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsHelpersV.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsHelpersV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 // Output variables
 vec3 getSunlitColor();
diff --git a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsV.glsl b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsV.glsl
index f5c513bbddc679ae50fdb18ece6026212c13d805..8d365c15caf4fedb58ee369ee58a83462703995d 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsV.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 // varying param funcs
 void setSunlitColor(vec3 v);
diff --git a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsF.glsl b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsF.glsl
index d0b60e918e9415e03d9fa7e9411dc50c04b569ac..cf9ef306324b1565d17e61496bce1b0da47e7f2c 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsF.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_PositionEye;
 
diff --git a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsV.glsl b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsV.glsl
index 4b4baf50d067e7531f5fec3810b31e1f2428dd17..398f1556a06925a21a2e18e15ad8a120af7573ca 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsV.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/atmosphericsVarsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec3 vary_PositionEye;
 
diff --git a/indra/newview/app_settings/shaders/class2/windlight/cloudsF.glsl b/indra/newview/app_settings/shaders/class2/windlight/cloudsF.glsl
index 2a559440fc4974953518d88da79c8ae57904099b..13207997b2f88b2f91842bec4a15a98ddb115804 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/cloudsF.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/cloudsF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 /////////////////////////////////////////////////////////////////////////
 // The fragment shader for the sky
diff --git a/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl b/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl
index 865c0e9da8aea28d1783b01e9ab4750c88e8a8bf..267ef36d4db63e6666d8cbf17a561546497d3eec 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 //////////////////////////////////////////////////////////////////////////
 // The vertex shader for creating the atmospheric sky
diff --git a/indra/newview/app_settings/shaders/class2/windlight/gammaF.glsl b/indra/newview/app_settings/shaders/class2/windlight/gammaF.glsl
index ce4bd2358fabe70b4b033d09ddebbd89a1f9ae41..a658edd21f82d539bf9235fa27ddd41dc5275459 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/gammaF.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/gammaF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec4 gamma;
 
diff --git a/indra/newview/app_settings/shaders/class2/windlight/skyF.glsl b/indra/newview/app_settings/shaders/class2/windlight/skyF.glsl
index b69a88a45f135442af865636e367e21aa50165ba..77ca4868a68a81eeeee79cece7cddddfe2cfd004 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/skyF.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/skyF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 /////////////////////////////////////////////////////////////////////////
 // The fragment shader for the sky
diff --git a/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl b/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl
index 397db01378d306e3c9ec1d48c42a4f7a03b4d10d..03bca8f27e2e2ec9c45cbb4386be0ee70fefc416 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 // SKY ////////////////////////////////////////////////////////////////////////
 // The vertex shader for creating the atmospheric sky
diff --git a/indra/newview/app_settings/shaders/class2/windlight/transportF.glsl b/indra/newview/app_settings/shaders/class2/windlight/transportF.glsl
index b30313bdc8cb310add08d8823c40efad1a033662..7f1ad4d5b4d31efb498056e130b7b6aacf6ead14 100644
--- a/indra/newview/app_settings/shaders/class2/windlight/transportF.glsl
+++ b/indra/newview/app_settings/shaders/class2/windlight/transportF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 //////////////////////////////////////////////////////////
 // The fragment shader for the terrain atmospherics
diff --git a/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl b/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
index c85ba0c7344879b3f87471a56f7a203738c38501..a003e2a1f10e521eaee6a9188bf5a0a69cc61ddc 100644
--- a/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
+++ b/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
 mat4 getSkinnedTransform();
diff --git a/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl b/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl
index d26b244fa348b847b87f9ab84e6cc924df7ca318..fc370ef367c3f60319f84c0027c5ecb4bf6cf77b 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2DRect giLightMap;
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/giDownsampleV.glsl b/indra/newview/app_settings/shaders/class3/deferred/giDownsampleV.glsl
index e5f621764413d68a121116dc890d7d938a363fc1..ae57227fe528b03b5e2a0d216c9a8b97a5ddfb39 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/giDownsampleV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/giDownsampleV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/giF.glsl b/indra/newview/app_settings/shaders/class3/deferred/giF.glsl
index 735150a78c853dd96b91554c829bd8278d67a5ed..951e3e97aefc88dee9408e01aad35d701c24d2d5 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/giF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/giF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/giFinalF.glsl b/indra/newview/app_settings/shaders/class3/deferred/giFinalF.glsl
index e0f4a3e4f5fe9f5d996dcceb08226f849b08035c..b2f8b2c6338b207c23270e896d50a87f58f6a619 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/giFinalF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/giFinalF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/giFinalV.glsl b/indra/newview/app_settings/shaders/class3/deferred/giFinalV.glsl
index fbf2c1737096f11d970d491f117923c402c405da..19c4e07b8b8dffa2791280efcf370e0940e5e306 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/giFinalV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/giFinalV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/giV.glsl b/indra/newview/app_settings/shaders/class3/deferred/giV.glsl
index 543527612ee540f8558eb9d63a928c9af443ff93..8dc1410ea5ad90d05d8bad4c485238550fd4cdd7 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/giV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/giV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/luminanceF.glsl b/indra/newview/app_settings/shaders/class3/deferred/luminanceF.glsl
index d9483bc6e43ad0f60e49d43f1b5decdf05b5ebd2..5f3bf68b240132fbe301d529805a06c9fa9e7b93 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/luminanceF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/luminanceF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/luminanceV.glsl b/indra/newview/app_settings/shaders/class3/deferred/luminanceV.glsl
index 6368def830087fb15ea4fe33491f9fa2f7d85e96..a24eda35dce7e099240dfc47dd37de2e5a1cb06b 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/luminanceV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/luminanceV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class3/deferred/postDeferredF.glsl
index 51ab579e3cc4d94925c160fe4836474581e3e458..ab99a889718571b597a502d811b8c30551d440ef 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/postDeferredF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/postDeferredF.glsl
@@ -4,7 +4,9 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
- 
+  
+#version 120
+
 #extension GL_ARB_texture_rectangle : enable
 
 uniform sampler2DRect diffuseRect;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/postDeferredV.glsl b/indra/newview/app_settings/shaders/class3/deferred/postDeferredV.glsl
index 0ec81dcb02ed61b19b8b93d6dd0356a00171a584..12983baa94882bfc3f58349847dd7bec1b3aadca 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/postDeferredV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/postDeferredV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/postgiF.glsl b/indra/newview/app_settings/shaders/class3/deferred/postgiF.glsl
index 24fa07f251fcd813a7eb8da9d3882d3eb5980cf5..f037754708726c727f9cff7a43598c3eb4d9e562 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/postgiF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/postgiF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/postgiV.glsl b/indra/newview/app_settings/shaders/class3/deferred/postgiV.glsl
index e5f621764413d68a121116dc890d7d938a363fc1..ae57227fe528b03b5e2a0d216c9a8b97a5ddfb39 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/postgiV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/postgiV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 varying vec2 vary_fragcoord;
 uniform vec2 screen_res;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
index a2db247331286af345f2e025a9bb287881a3f685..ce32f66000f91fa0b67400b931f3da6464d669c7 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 #extension GL_ARB_texture_rectangle : enable
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightV.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightV.glsl
index 9d187b46e208ba0b70005c661fc342ec013bd271..8f0bcca76b86a22f32101d0c81b3631110fff0c9 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightV.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform vec2 screen_res;
 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl
index 1c1725a95c581f3612fb2c60bac3ec6f1fe59951..c54d9a1e3e80dcf0bc9ba20d70c92b346d4fa3dc 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 uniform sampler2D diffuseMap;
 
diff --git a/indra/newview/app_settings/shaders/class3/lighting/sumLightsSpecularV.glsl b/indra/newview/app_settings/shaders/class3/lighting/sumLightsSpecularV.glsl
index 2b44aedd5aee85c32399c9d853a06778830e77b6..04533fdce1ac78d75e8d724802d07a4aba5e2d3e 100644
--- a/indra/newview/app_settings/shaders/class3/lighting/sumLightsSpecularV.glsl
+++ b/indra/newview/app_settings/shaders/class3/lighting/sumLightsSpecularV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da);
 vec3 calcPointLightSpecular(inout vec4 specular, vec3 view, vec3 v, vec3 n, vec3 l, float r, float pw, vec3 lightCol);
diff --git a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl
index 329b0c4305b5b2d644a19d4ea32b6455993b83b5..73bc18b8663aa6de17e17800558da1d7d5e0a50f 100644
--- a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl
+++ b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl
@@ -4,6 +4,8 @@
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * $/LicenseInfo$
  */
+ 
+#version 120
 
 float calcDirectionalLight(vec3 n, vec3 l);
 float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
diff --git a/indra/newview/app_settings/ultra_graphics.xml b/indra/newview/app_settings/ultra_graphics.xml
index e1f3ca5769995e5b7327c54b964b317a4a994f6c..71459e5470a6dddecbcaba616db74b87aebd2340 100644
--- a/indra/newview/app_settings/ultra_graphics.xml
+++ b/indra/newview/app_settings/ultra_graphics.xml
@@ -4,15 +4,15 @@
 	<RenderAvatarCloth value="TRUE"/>
 	<!--Default for now-->
 	<RenderAvatarLODFactor value="1.0"/>
-  <!--Default for now-->
-  <RenderAvatarPhysicsLODFactor value="1.0"/>
+	<!--Default for now-->
+	<RenderAvatarPhysicsLODFactor value="1.0"/>
 	<!--NO SHADERS-->
 	<RenderAvatarVP value="TRUE"/>
 	<!--Short Range-->
 	<RenderFarClip value="256"/>
 	<!--Default for now-->
 	<RenderFlexTimeFactor value="1"/>
-	<!--256... but they don't use this-->
+	<!--256... but they do not use this-->
 	<RenderGlowResolutionPow value="9"/>
 	<!--Low number-->
 	<RenderMaxPartCount value="4096"/>
@@ -26,8 +26,6 @@
 	<RenderTerrainLODFactor value="2.0"/>
 	<!--Default for now-->
 	<RenderTreeLODFactor value="1.0"/>
-	<!--Default for now-->
-	<RenderUseFBO value="1"/>
 	<!--Try Impostors-->
 	<RenderUseImpostors value="TRUE"/>
 	<!--Default for now-->
@@ -36,11 +34,10 @@
 	<VertexShaderEnable value="TRUE"/>
 	<!--NO SHADERS-->
 	<WindLightUseAtmosShaders value="TRUE"/>
-  <!--Deferred Shading-->
-  <RenderDeferred value="TRUE"/>
-  <!--SSAO Enabled-->
-  <RenderDeferredSSAO value="TRUE"/>
-  <!--Full Shadows-->
-  <RenderShadowDetail value="2"/>
-
+	<!--Deferred Shading-->
+	<RenderDeferred value="TRUE"/>
+	<!--SSAO Enabled-->
+	<RenderDeferredSSAO value="TRUE"/>
+	<!--Full Shadows-->
+	<RenderShadowDetail value="2"/>
 </settings>
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index af2d951bf70988de588ac7949c342213f46222d4..dd8a88e5586036e14b46bf6ffbeca97485cd83eb 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -1,4 +1,4 @@
-version 25
+version 27
 
 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences
 // Should be combined into one table
@@ -40,6 +40,7 @@ RenderGround				1	1
 RenderMaxPartCount			1	8192
 RenderNightBrightness		1	1.0
 RenderObjectBump			1	1
+RenderLocalLights			1	1
 RenderReflectionDetail		1	4
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	2.0
@@ -57,13 +58,12 @@ Disregard128DefaultDrawDistance	1	1
 Disregard96DefaultDrawDistance	1	1
 RenderTextureMemoryMultiple		1	1.0
 RenderShaderLightingMaxLevel	1	3
+RenderDeferred				1	1
 SkyUseClassicClouds			1	1
-RenderDeferred				1	0
-RenderDeferredSSAO			1	0
-RenderShadowDetail			1	0
+RenderDeferredSSAO			1	1
+RenderShadowDetail			1	2
 WatchdogDisabled				1	1
 RenderUseStreamVBO			1	1
-RenderUseFBO				1	1
 
 //
 // Low Graphics Settings
@@ -80,6 +80,7 @@ RenderFlexTimeFactor		1	0
 RenderGlowResolutionPow		1	8
 RenderMaxPartCount			1	0
 RenderObjectBump			1	0
+RenderLocalLights			1	0
 RenderReflectionDetail		1	0
 RenderTerrainDetail			1	0
 RenderTerrainLODFactor		1	1
@@ -94,7 +95,6 @@ SkyUseClassicClouds			1	0
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // Mid Graphics Settings
@@ -110,6 +110,7 @@ RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	8
 RenderMaxPartCount			1	2048
 RenderObjectBump			1	1
+RenderLocalLights			1	1
 RenderReflectionDetail		1	0
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	1.0
@@ -123,7 +124,6 @@ WLSkyDetail					1	48
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // High Graphics Settings (purty)
@@ -139,6 +139,7 @@ RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
 RenderMaxPartCount			1	4096
 RenderObjectBump			1	1
+RenderLocalLights			1	1
 RenderReflectionDetail		1	0
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	2.0
@@ -152,7 +153,6 @@ WLSkyDetail					1	48
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -167,6 +167,7 @@ RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
 RenderMaxPartCount			1	8192
 RenderObjectBump			1	1
+RenderLocalLights			1	1
 RenderReflectionDetail		1	4
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	2.0
@@ -177,10 +178,9 @@ RenderVolumeLODFactor		1	2.0
 VertexShaderEnable			1	1
 WindLightUseAtmosShaders	1	1
 WLSkyDetail					1	128
-RenderDeferred				1	0
-RenderDeferredSSAO			1	0
-RenderShadowDetail			1	0
-RenderUseFBO				1	0
+RenderDeferred				1	1
+RenderDeferredSSAO			1	1
+RenderShadowDetail			1	2
 
 //
 // Class Unknown Hardware (unknown)
@@ -238,6 +238,7 @@ RenderDeferred				0	0
 RenderDeferredSSAO			0	0
 RenderShadowDetail			0	0
 
+
 //
 // "Default" setups for safe, low, medium, high
 //
@@ -246,6 +247,7 @@ RenderAnisotropic			1	0
 RenderAvatarCloth			0	0
 RenderAvatarVP				0	0
 RenderObjectBump			0	0
+RenderLocalLights			1	0
 RenderMaxPartCount			1	1024
 RenderTerrainDetail 		1	0
 RenderUseImpostors			0	0
@@ -255,8 +257,8 @@ WindLightUseAtmosShaders	0	0
 RenderDeferred				0	0
 RenderDeferredSSAO			0	0
 RenderShadowDetail			0	0
-RenderUseFBO				1	0
 		
+
 //
 // CPU based feature masks
 //
@@ -277,6 +279,9 @@ RenderObjectBump			0	0
 list OpenGLPre15
 RenderVBOEnable				1	0
 
+list OpenGLPre30
+RenderDeferred				0	0
+
 list Intel
 RenderAnisotropic			1	0
 
@@ -462,6 +467,11 @@ RenderAvatarCloth			0	0
 
 list ATI
 RenderUseStreamVBO			1	0
+RenderAvatarVP				1	0
+
+// Disable vertex buffer objects by default for ATI cards with little video memory
+list ATIVramLT256
+RenderVBOEnable				1	0
 
 /// Tweaked NVIDIA
 
@@ -562,3 +572,4 @@ list NVIDIA_GeForce_Go_7800
 RenderShaderLightingMaxLevel	1	2
 list NVIDIA_GeForce_Go_7900
 RenderShaderLightingMaxLevel	1	2
+
diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt
index 5da1495da9aade25f555de0420daef2a3675b4a6..058bdcc7308bf9a35709c3304afb724491a06852 100644
--- a/indra/newview/featuretable_linux.txt
+++ b/indra/newview/featuretable_linux.txt
@@ -1,4 +1,4 @@
-version 22
+version 23
 
 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences
 // Should be combined into one table
@@ -37,6 +37,7 @@ RenderFogRatio				1	4.0
 RenderGamma					1	0
 RenderGlowResolutionPow		1	9
 RenderGround				1	1
+RenderLocalLights			1	1
 RenderMaxPartCount			1	8192
 RenderNightBrightness		1	1.0
 RenderObjectBump			1	1
@@ -58,11 +59,9 @@ Disregard96DefaultDrawDistance	1	1
 RenderTextureMemoryMultiple		1	1.0
 SkyUseClassicClouds			1	1
 RenderShaderLightingMaxLevel		1	3
-RenderDeferred				1	0
-RenderDeferredSSAO			1	0
-RenderShadowDetail			1	0
-WatchdogDisabled				1	1
-RenderUseFBO				1	1
+RenderDeferred				1	1
+RenderDeferredSSAO			1	1
+RenderShadowDetail			1	2
 
 //
 // Low Graphics Settings
@@ -77,6 +76,7 @@ RenderAvatarVP				1	0
 RenderFarClip				1	64
 RenderFlexTimeFactor		1	0
 RenderGlowResolutionPow		1	8
+RenderLocalLights			1	0
 RenderMaxPartCount			1	0
 RenderObjectBump			1	0
 RenderReflectionDetail		1	0
@@ -93,7 +93,6 @@ SkyUseClassicClouds			1	0
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // Mid Graphics Settings
@@ -107,6 +106,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	96
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	8
+RenderLocalLights			1	1
 RenderMaxPartCount			1	2048
 RenderObjectBump			1	1
 RenderReflectionDetail		1	0
@@ -122,7 +122,6 @@ WLSkyDetail					1	48
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // High Graphics Settings (purty)
@@ -136,6 +135,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	128
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
+RenderLocalLights			1	1
 RenderMaxPartCount			1	4096
 RenderObjectBump			1	1
 RenderReflectionDetail		1	0
@@ -151,7 +151,6 @@ WLSkyDetail					1	48
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -165,6 +164,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	256
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
+RenderLocalLights			1	1
 RenderMaxPartCount			1	8192
 RenderObjectBump			1	1
 RenderReflectionDetail		1	4
@@ -177,10 +177,9 @@ RenderVolumeLODFactor		1	2.0
 VertexShaderEnable			1	1
 WindLightUseAtmosShaders	1	1
 WLSkyDetail					1	128
-RenderDeferred				1	0
-RenderDeferredSSAO			1	0
-RenderShadowDetail			1	0
-RenderUseFBO				1	0
+RenderDeferred				1	1
+RenderDeferredSSAO			1	1
+RenderShadowDetail			1	2
 
 //
 // Class Unknown Hardware (unknown)
@@ -255,7 +254,6 @@ WindLightUseAtmosShaders	0	0
 RenderDeferred				0	0
 RenderDeferredSSAO			0	0
 RenderShadowDetail			0	0
-RenderUseFBO				1	0
 
 
 //
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index 421f9c097391b8e6326a30ea02f278fd06557fa8..c075c660f32a17de4ee5269414f831d4e8657aa5 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -1,4 +1,4 @@
-version 22
+version 23
 
 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences
 // Should be combined into one table
@@ -24,11 +24,11 @@ version 22
 //
 list all
 RenderAnisotropic				1	0
-RenderAvatarCloth				0	0
+RenderAvatarCloth				1	1
 RenderAvatarLODFactor			1	1.0
 RenderAvatarPhysicsLODFactor 1	1.0
 RenderAvatarMaxVisible          1   12
-RenderAvatarVP					1	0
+RenderAvatarVP					1	1
 RenderCubeMap					1	1
 RenderDelayVBUpdate				1	0
 RenderFarClip					1	256
@@ -37,11 +37,11 @@ RenderFogRatio					1	4.0
 RenderGamma						1	0
 RenderGlowResolutionPow			1	9
 RenderGround					1	1
-RenderLightingDetail			1	1
+RenderLocalLights				1	1
 RenderMaxPartCount				1	8192
 RenderNightBrightness			1	1.0
 RenderObjectBump				1	1
-RenderReflectionDetail			1	3
+RenderReflectionDetail			1	4
 RenderTerrainDetail				1	1
 RenderTerrainLODFactor			1	2.0
 RenderTransparentWater			1	1
@@ -49,20 +49,21 @@ RenderTreeLODFactor				1	1.0
 RenderUseImpostors				1	1
 RenderVBOEnable					1	1
 RenderVolumeLODFactor			1	2.0
-RenderWaterReflections			1	1
+UseStartScreen				1	1
 UseOcclusion					1	1
 VertexShaderEnable				1	1
 WindLightUseAtmosShaders		1	1
 WLSkyDetail						1	128
-RenderUseCleverUI				1	1
 Disregard128DefaultDrawDistance	1	1
 Disregard96DefaultDrawDistance	1	1
 RenderTextureMemoryMultiple		1	0.5
-Disregard128DefaultDrawDistance	1	1
-Disregard96DefaultDrawDistance	1	1
+RenderShaderLightingMaxLevel	1	3
 SkyUseClassicClouds			1	1
+RenderDeferred				1	1
+RenderDeferredSSAO			1	1
+RenderShadowDetail			1	2
 WatchdogDisabled				1	1
-RenderUseFBO				1	1
+RenderUseStreamVBO			1	1
 
 //
 // Low Graphics Settings
@@ -77,7 +78,7 @@ RenderAvatarVP				1	0
 RenderFarClip				1	64
 RenderFlexTimeFactor		1	0
 RenderGlowResolutionPow		1	8
-RenderLightingDetail		1	0
+RenderLocalLights			1	0
 RenderMaxPartCount			1	0
 RenderObjectBump			1	0
 RenderReflectionDetail		1	0
@@ -87,12 +88,13 @@ RenderTransparentWater		1	0
 RenderTreeLODFactor			1	0
 RenderUseImpostors			1	1
 RenderVolumeLODFactor		1	0.5
-RenderWaterReflections		1	0
 VertexShaderEnable			1	0
 WindLightUseAtmosShaders	1	0
 WLSkyDetail					1	48
 SkyUseClassicClouds			1	0
-RenderUseFBO				1	0
+RenderDeferred				1	0
+RenderDeferredSSAO			1	0
+RenderShadowDetail			1	0
 
 //
 // Mid Graphics Settings
@@ -106,7 +108,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	96
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	8
-RenderLightingDetail		1	1
+RenderLocalLights			1	1
 RenderMaxPartCount			1	2048
 RenderObjectBump			1	1
 RenderReflectionDetail		1	0
@@ -116,11 +118,12 @@ RenderTransparentWater		1	1
 RenderTreeLODFactor			1	0.5
 RenderUseImpostors			1	1
 RenderVolumeLODFactor		1	1.125
-RenderWaterReflections		1	0
 VertexShaderEnable			1	1
 WindLightUseAtmosShaders	1	0
 WLSkyDetail					1	48
-RenderUseFBO				1	0
+RenderDeferred				1	0
+RenderDeferredSSAO			1	0
+RenderShadowDetail			1	0
 
 //
 // High Graphics Settings (purty)
@@ -134,7 +137,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	128
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
-RenderLightingDetail		1	1
+RenderLocalLights			1	1
 RenderMaxPartCount			1	4096
 RenderObjectBump			1	1
 RenderReflectionDetail		1	0
@@ -144,11 +147,12 @@ RenderTransparentWater		1	1
 RenderTreeLODFactor			1	0.5
 RenderUseImpostors			1	1
 RenderVolumeLODFactor		1	1.125
-RenderWaterReflections		1	0
 VertexShaderEnable			1	1
 WindLightUseAtmosShaders	1	1
 WLSkyDetail					1	48
-RenderUseFBO				1	0
+RenderDeferred				1	0
+RenderDeferredSSAO			1	0
+RenderShadowDetail			1	2
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -162,21 +166,22 @@ RenderAvatarVP				1	1
 RenderFarClip				1	256
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
-RenderLightingDetail		1	1
+RenderLocalLights			1	1
 RenderMaxPartCount			1	8192
 RenderObjectBump			1	1
-RenderReflectionDetail		1	3
+RenderReflectionDetail		1	4
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	2.0
 RenderTransparentWater		1	1
 RenderTreeLODFactor			1	1.0
 RenderUseImpostors			1	1
 RenderVolumeLODFactor		1	2.0
-RenderWaterReflections		1	1
 VertexShaderEnable			1	1
 WindLightUseAtmosShaders	1	1
 WLSkyDetail					1	128
-RenderUseFBO				1	0
+RenderDeferred				1	0
+RenderDeferredSSAO			1	0
+RenderShadowDetail			1	2
 
 //
 // Class Unknown Hardware (unknown)
@@ -214,9 +219,12 @@ RenderVBOEnable				1	1
 list NoPixelShaders
 RenderAvatarVP				0	0
 RenderAvatarCloth			0	0
-RenderWaterReflections		0	0
+RenderReflectionDetail		0	0
 VertexShaderEnable			0	0
 WindLightUseAtmosShaders	0	0
+RenderDeferred				0	0
+RenderDeferredSSAO			0	0
+RenderShadowDetail			0	0
 
 //
 // No Vertex Shaders available
@@ -224,25 +232,31 @@ WindLightUseAtmosShaders	0	0
 list NoVertexShaders
 RenderAvatarVP				0	0
 RenderAvatarCloth			0	0
-RenderWaterReflections		0	0
+RenderReflectionDetail		0	0
 VertexShaderEnable			0	0
 WindLightUseAtmosShaders	0	0
+RenderDeferred				0	0
+RenderDeferredSSAO			0	0
+RenderShadowDetail			0	0
 
+//
 // "Default" setups for safe, low, medium, high
 //
 list safe
 RenderAnisotropic			1	0
 RenderAvatarCloth			0	0
 RenderAvatarVP				0	0
-RenderLightingDetail		1	0
+RenderLocalLights			1	0
 RenderObjectBump			0	0
 RenderMaxPartCount			1	1024
 RenderTerrainDetail 		1	0
 RenderUseImpostors			0	0
 RenderVBOEnable				1	0
-RenderWaterReflections		0	0
+RenderReflectionDetail		0	0
 WindLightUseAtmosShaders	0	0
-RenderUseFBO				1	0
+RenderDeferred				0	0
+RenderDeferredSSAO			0	0
+RenderShadowDetail			0	0
 
 //
 // CPU based feature masks
@@ -264,13 +278,16 @@ RenderObjectBump			0	0
 list OpenGLPre15
 RenderVBOEnable				1	0
 
+list TexUnit8orLess
+RenderDeferredSSAO			0	0
+
 list Intel
 RenderAnisotropic			1	0
-RenderLightingDetail		1	0
+RenderLocalLights			1	0
 
 list GeForce2
 RenderAnisotropic			1	0
-RenderLightingDetail		1	0
+RenderLocalLights			1	0
 RenderMaxPartCount			1	2048
 RenderTerrainDetail			1	0
 RenderVBOEnable				1	1
@@ -387,7 +404,6 @@ list ATI_Radeon_X1500
 Disregard128DefaultDrawDistance	1	0
 list ATI_Radeon_X1600 
 Disregard128DefaultDrawDistance	1	0
-RenderUseFBO 					0	0
 list ATI_Radeon_X1700 
 Disregard128DefaultDrawDistance	1	0
 list ATI_Mobility_Radeon_X1xxx
diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt
index c2e5dfff9fe17fa6df246b167449a8ed763cab9b..3339172a1a3d49ead0f4562b06c10af0974e05b6 100644
--- a/indra/newview/featuretable_xp.txt
+++ b/indra/newview/featuretable_xp.txt
@@ -1,4 +1,4 @@
-version 25
+version 27
 
 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences
 // Should be combined into one table
@@ -37,6 +37,7 @@ RenderFogRatio				1	4.0
 RenderGamma					1	0
 RenderGlowResolutionPow		1	9
 RenderGround				1	1
+RenderLocalLights			1	1
 RenderMaxPartCount			1	8192
 RenderNightBrightness		1	1.0
 RenderObjectBump			1	1
@@ -61,7 +62,6 @@ SkyUseClassicClouds			1	1
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	1
 WatchdogDisabled				1	1
 RenderUseStreamVBO			1	1
 
@@ -78,6 +78,7 @@ RenderAvatarVP				1	0
 RenderFarClip				1	64
 RenderFlexTimeFactor		1	0
 RenderGlowResolutionPow		1	8
+RenderLocalLights			1	0
 RenderMaxPartCount			1	0
 RenderObjectBump			1	0
 RenderReflectionDetail		1	0
@@ -94,7 +95,6 @@ SkyUseClassicClouds			1	0
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // Mid Graphics Settings
@@ -108,6 +108,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	96
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	8
+RenderLocalLights			1	1
 RenderMaxPartCount			1	2048
 RenderObjectBump			1	1
 RenderReflectionDetail		1	0
@@ -123,7 +124,6 @@ WLSkyDetail					1	48
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
 RenderShadowDetail			1	0
-RenderUseFBO				1	0
 
 //
 // High Graphics Settings (purty)
@@ -137,6 +137,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	128
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
+RenderLocalLights			1	1
 RenderMaxPartCount			1	4096
 RenderObjectBump			1	1
 RenderReflectionDetail		1	0
@@ -151,8 +152,7 @@ WindLightUseAtmosShaders	1	1
 WLSkyDetail					1	48
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
-RenderShadowDetail			1	0
-RenderUseFBO				1	0
+RenderShadowDetail			1	2
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -166,6 +166,7 @@ RenderAvatarVP				1	1
 RenderFarClip				1	256
 RenderFlexTimeFactor		1	1.0
 RenderGlowResolutionPow		1	9
+RenderLocalLights			1	1
 RenderMaxPartCount			1	8192
 RenderObjectBump			1	1
 RenderReflectionDetail		1	4
@@ -180,8 +181,7 @@ WindLightUseAtmosShaders	1	1
 WLSkyDetail					1	128
 RenderDeferred				1	0
 RenderDeferredSSAO			1	0
-RenderShadowDetail			1	0
-RenderUseFBO				1	0
+RenderShadowDetail			1	2
 
 //
 // Class Unknown Hardware (unknown)
@@ -256,7 +256,6 @@ WindLightUseAtmosShaders	0	0
 RenderDeferred				0	0
 RenderDeferredSSAO			0	0
 RenderShadowDetail			0	0
-RenderUseFBO				1	0
 
 //
 // CPU based feature masks
@@ -278,6 +277,9 @@ RenderObjectBump			0	0
 list OpenGLPre15
 RenderVBOEnable				1	0
 
+list OpenGLPre30
+RenderDeferred				0	0
+
 list Intel
 RenderAnisotropic			1	0
 
@@ -464,6 +466,11 @@ RenderAvatarCloth			0	0
 
 list ATI
 RenderUseStreamVBO			1	0
+RenderAvatarVP				1	0
+
+// Disable vertex buffer objects by default for ATI cards with little video memory
+list ATIVramLT256
+RenderVBOEnable				1	0
 
 /// Tweaked NVIDIA
 
diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt
index da888bc64dbefd391cdd33b2a840f1478da88744..66b3b97f005441c7d9dd30ec6dc92e8efc4013fc 100644
--- a/indra/newview/gpu_table.txt
+++ b/indra/newview/gpu_table.txt
@@ -207,6 +207,7 @@ NVIDIA GTX 280					.*NVIDIA.*GeForce GTX 28.*			3		1
 NVIDIA GTX 290					.*NVIDIA.*GeForce GTX 29.*			3		1
 NVIDIA GTX 470					.*NVIDIA.*GeForce GTX 47.*			3		1
 NVIDIA GTX 480					.*NVIDIA.*GeForce GTX 48.*			3		1
+NVIDIA GTX 580					.*NVIDIA.*GeForce GTX 58.*			3		1
 NVIDIA C51						.*NVIDIA.*C51.*						0		1
 NVIDIA G72						.*NVIDIA.*G72.*						1		1
 NVIDIA G73						.*NVIDIA.*G73.*						1		1
@@ -301,12 +302,43 @@ NVIDIA NV43						.*NVIDIA.*NV43.*					1		1
 NVIDIA NV44						.*NVIDIA.*NV44.*					1		1
 NVIDIA nForce					.*NVIDIA.*nForce.*					0		0
 NVIDIA MCP78					.*NVIDIA.*MCP78.*					1		1
-NVIDIA Quadro2					.*Quadro2.*							0		1
-NVIDIA Quadro4					.*Quadro4.*							0		1
-NVIDIA Quadro DCC				.*Quadro DCC.*						0		1
-NVIDIA Quadro FX 4500			.*Quadro.*FX.*4500.*				3		1
-NVIDIA Quadro FX				.*Quadro FX.*						1		1
-NVIDIA Quadro NVS				.*Quadro NVS.*						0		1
+NVIDIA Quadro2					.*Quadro2.*				0		1
+NVIDIA Quadro4					.*Quadro4.*				0		1
+NVIDIA Quadro DCC				.*Quadro DCC.*				0		1
+NVIDIA Quadro FX 1400				.*Quadro.*FX.*1400.*			1		1
+NVIDIA Quadro FX 1500				.*Quadro.*FX.*1500.*			1		1
+NVIDIA Quadro FX 1700				.*Quadro.*FX.*1700.*			2		1
+NVIDIA Quadro FX 1800				.*Quadro.*FX.*1800.*			2		1
+NVIDIA Quadro FX 3400				.*Quadro.*FX.*3400.*			1		1
+NVIDIA Quadro FX 3450				.*Quadro.*FX.*3450.*			1		1
+NVIDIA Quadro FX 3500				.*Quadro.*FX.*3500.*			1		1
+NVIDIA Quadro FX 3700				.*Quadro.*FX.*3700.*			2		1
+NVIDIA Quadro FX 3800				.*Quadro.*FX.*3800.*			2		1
+NVIDIA Quadro FX 370				.*Quadro.*FX.*370.*			2		1
+NVIDIA Quadro FX 380				.*Quadro.*FX.*380.*			2		1
+NVIDIA Quadro FX 4000				.*Quadro.*FX.*4000.*			1		1
+NVIDIA Quadro FX 4500				.*Quadro.*FX.*4500.*			1		1
+NVIDIA Quadro FX 4600				.*Quadro.*FX.*4600.*			2		1
+NVIDIA Quadro FX 4700				.*Quadro.*FX.*4700.*			2		1
+NVIDIA Quadro FX 4800				.*Quadro.*FX.*4800.*			2		1
+NVIDIA Quadro FX 470				.*Quadro.*FX.*470.*			2		1
+NVIDIA Quadro FX 5500				.*Quadro.*FX.*5500.*			1		1
+NVIDIA Quadro FX 5600				.*Quadro.*FX.*5600.*			2		1
+NVIDIA Quadro FX 5700				.*Quadro.*FX.*5700.*			2		1
+NVIDIA Quadro FX 5800				.*Quadro.*FX.*5800.*			2		1
+NVIDIA Quadro FX 540				.*Quadro.*FX.*540.*			1		1
+NVIDIA Quadro FX 550				.*Quadro.*FX.*550.*			1		1
+NVIDIA Quadro FX 560				.*Quadro.*FX.*560.*			1		1
+NVIDIA Quadro FX 570				.*Quadro.*FX.*570.*			2		1
+NVIDIA Quadro FX 580				.*Quadro.*FX.*580.*			2		1
+NVIDIA Quadro FX				.*Quadro FX.*				0		1
+NVIDIA Quadro VX 200				.*Quadro VX.*200.*			2		1
+NVIDIA Quadro 2000				.*Quadro.*2000.*			2		1
+NVIDIA Quadro 4000				.*Quadro.*4000.*			2		1
+NVIDIA Quadro 5000				.*Quadro.*5000.*			2		1
+NVIDIA Quadro 6000				.*Quadro.*6000.*			2		1
+NVIDIA Quadro 600				.*Quadro.*600.*				2		1
+NVIDIA Quadro NVS				.*Quadro NVS.*				0		1
 NVIDIA RIVA TNT					.*RIVA TNT.*						0		0
 S3								.*S3 Graphics.*						0		0
 SiS								SiS.*								0		0
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index 4e8ed807eeef65541d288055ff4dbb948fd91b9b..b5d43021ec323799bed2cc5a0ef832f9c6bc6389 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -210,6 +210,25 @@ continue_install:
     Return
 FunctionEnd
 	
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Checks for CPU valid (must have SSE2 support)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Function CheckCPUFlags
+    Call GetWindowsVersion
+    Pop $R0
+    StrCmp $R0 "2000" OK_SSE  ; sse check not available on win2k.
+
+    Push $1
+    System::Call 'kernel32::IsProcessorFeaturePresent(i) i(10) .r1'
+    IntCmp $1 1 OK_SSE
+    MessageBox MB_OKCANCEL $(MissingSSE2) /SD IDOK IDOK OK_SSE
+    Quit
+
+  OK_SSE:
+    Pop $1
+    Return
+FunctionEnd
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Close the program, if running. Modifies no variables.
 ; Allows user to bail out of install process.
@@ -744,6 +763,7 @@ StrCpy $INSTEXE "${INSTEXE}"
 StrCpy $INSTSHORTCUT "${SHORTCUT}"
 
 Call CheckWindowsVersion		; warn if on Windows 98/ME
+Call CheckCPUFlags			; Make sure we have SSE2 support
 Call CheckIfAdministrator		; Make sure the user can install/uninstall
 Call CheckIfAlreadyCurrent		; Make sure that we haven't already installed this version
 Call CloseSecondLife			; Make sure we're not running
diff --git a/indra/newview/installers/windows/lang_en-us.nsi b/indra/newview/installers/windows/lang_en-us.nsi
index a01541377dfd717d76d318989593d302add98d94..da0d7f54d2bb848ec0f92d08d6a0bcb9926f734a 100644
Binary files a/indra/newview/installers/windows/lang_en-us.nsi and b/indra/newview/installers/windows/lang_en-us.nsi differ
diff --git a/indra/newview/licenses-win32.txt b/indra/newview/licenses-win32.txt
index 7e6d4b4561ce44100f2a395517ccf3964e85acf2..8736626907409eb21518ba10793faa2793cbef3a 100644
--- a/indra/newview/licenses-win32.txt
+++ b/indra/newview/licenses-win32.txt
@@ -769,3 +769,72 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+=============
+GLOD license
+=============
+The GLOD Open-Source License   Version 1.0                June 16, 2004
+
+Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns 
+Hopkins University and David Luebke, Brenden Schubert, University of 
+Virginia. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer and
+   request.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer and
+   request in the documentation and/or other materials provided with
+   the distribution.
+
+3. The name "GLOD" must not be used to endorse or promote products
+   derived from this software without prior written permission.
+
+4. Redistributions of any modified version of this source, whether in
+   source or binary form , must include a form of the following
+   acknowledgment: "This product is derived from the GLOD library,
+   which is available from http://www.cs.jhu.edu/~graphics/GLOD."
+
+5. Redistributions of any modified version of this source in binary
+   form must provide, free of charge, access to the modified version
+   of the code.
+
+6. This license shall be governed by and construed and enforced in
+   accordance with the laws of the State of Maryland, without
+   reference to its conflicts of law provisions. The exclusive
+   jurisdiction and venue for all legal actions relating to this
+   license shall be in courts of competent subject matter jurisdiction
+   located in the State of Maryland.
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, GLOD IS PROVIDED
+UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+THAT GLOD IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
+PURPOSE OR NON-INFRINGING. ALL WARRANTIES ARE DISCLAIMED AND THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE CODE IS WITH
+YOU. SHOULD ANY CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
+COPYRIGHT HOLDER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
+NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY
+CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY CODE IS
+AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
+THE COPYRIGHT HOLDER OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES FOR LOSS OF
+PROFITS, REVENUE, OR FOR LOSS OF INFORMATION OR ANY OTHER LOSS.
+
+YOU EXPRESSLY AGREE TO FOREVER INDEMNIFY, DEFEND AND HOLD HARMLESS THE
+COPYRIGHT HOLDERS AND CONTRIBUTORS OF GLOD AGAINST ALL CLAIMS,
+DEMANDS, SUITS OR OTHER ACTIONS ARISING DIRECTLY OR INDIRECTLY FROM
+YOUR ACCEPTANCE AND USE OF GLOD.
+
+Although NOT REQUIRED, we would appreciate it if active users of GLOD
+put a link on their web site to the GLOD web site when possible.
+
+
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index d2df968544011e9f9ae4d2b18c8f1925c115f09f..283a28a0aa47b8c22401ff76c274afedbbf90a00 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -92,23 +92,23 @@ cd "${RUN_PATH}"
 ##  subprocesses that care.
 export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
 
-if [ -n "$LL_TCMALLOC" ]; then
-    tcmalloc_libs='/usr/lib/libtcmalloc.so.0 /usr/lib/libstacktrace.so.0 /lib/libpthread.so.0'
-    all=1
-    for f in $tcmalloc_libs; do
-        if [ ! -f $f ]; then
-	    all=0
-	fi
-    done
-    if [ $all != 1 ]; then
-        echo 'Cannot use tcmalloc libraries: components missing' 1>&2
-    else
-	export LD_PRELOAD=$(echo $tcmalloc_libs | tr ' ' :)
-	if [ -z "$HEAPCHECK" -a -z "$HEAPPROFILE" ]; then
-	    export HEAPCHECK=${HEAPCHECK:-normal}
-	fi
-    fi
-fi
+# if [ -n "$LL_TCMALLOC" ]; then
+#    tcmalloc_libs='/usr/lib/libtcmalloc.so.0 /usr/lib/libstacktrace.so.0 /lib/libpthread.so.0'
+#    all=1
+#    for f in $tcmalloc_libs; do
+#        if [ ! -f $f ]; then
+#	    all=0
+#	fi
+#    done
+#    if [ $all != 1 ]; then
+#        echo 'Cannot use tcmalloc libraries: components missing' 1>&2
+#    else
+#	export LD_PRELOAD=$(echo $tcmalloc_libs | tr ' ' :)
+#	if [ -z "$HEAPCHECK" -a -z "$HEAPPROFILE" ]; then
+#	    export HEAPCHECK=${HEAPCHECK:-normal}
+#	fi
+#    fi
+#fi
 
 export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:"${LD_LIBRARY_PATH}"'
 export SL_CMD='$LL_WRAPPER bin/do-not-directly-run-secondlife-bin'
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 8f5efcf941768d4f7755b5ef70a40cd031ea5599..08d71fc8fcbfcfcc80f921798f8097344b07500f 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -1224,7 +1224,13 @@ BOOL LLAgent::getBusy() const
 //-----------------------------------------------------------------------------
 // startAutoPilotGlobal()
 //-----------------------------------------------------------------------------
-void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::string& behavior_name, const LLQuaternion *target_rotation, void (*finish_callback)(BOOL, void *),  void *callback_data, F32 stop_distance, F32 rot_threshold)
+void LLAgent::startAutoPilotGlobal(
+	const LLVector3d &target_global,
+	const std::string& behavior_name,
+	const LLQuaternion *target_rotation,
+	void (*finish_callback)(BOOL, void *),
+	void *callback_data,
+	F32 stop_distance, F32 rot_threshold)
 {
 	if (!isAgentAvatarValid())
 	{
@@ -1255,7 +1261,7 @@ void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::s
 	else
 	{
 		// Guess at a reasonable stop distance.
-		mAutoPilotStopDistance = fsqrtf( distance );
+		mAutoPilotStopDistance = (F32) sqrt( distance );
 		if (mAutoPilotStopDistance < 0.5f) 
 		{
 			mAutoPilotStopDistance = 0.5f;
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 6c5c3bcdab1b54242d4435d2c019eb87b2b60366..c6b5a0113ff9d2b2f3d0ea49916700e459144acf 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -390,10 +390,12 @@ LLVector3 LLAgentCamera::calcFocusOffset(LLViewerObject *object, LLVector3 origi
 	{
 		return original_focus_point - obj_pos;
 	}
-
 	
 	LLQuaternion inv_obj_rot = ~obj_rot; // get inverse of rotation
-	LLVector3 object_extents = object->getScale();
+	LLVector3 object_extents;	
+	const LLVector4a* oe4 = object->mDrawable->getSpatialExtents();
+	object_extents.set( oe4[1][0], oe4[1][1], oe4[1][2] );
+	
 	// make sure they object extents are non-zero
 	object_extents.clamp(0.001f, F32_MAX);
 
@@ -551,7 +553,9 @@ BOOL LLAgentCamera::calcCameraMinDistance(F32 &obj_min_distance)
 {
 	BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars)
 
-	if (!mFocusObject || mFocusObject->isDead())
+	if (!mFocusObject || mFocusObject->isDead() || 
+		mFocusObject->isMesh() ||
+		gSavedSettings.getBOOL("DisableCameraConstraints"))
 	{
 		obj_min_distance = 0.f;
 		return TRUE;
diff --git a/indra/newview/llagentpilot.cpp b/indra/newview/llagentpilot.cpp
index 13e10231858dd1ff5e944a8f1aca2b6bae48fbf5..734c502fcf69676804fd2b2328abfeb39bb3b257 100644
--- a/indra/newview/llagentpilot.cpp
+++ b/indra/newview/llagentpilot.cpp
@@ -34,12 +34,12 @@
 #include "llagent.h"
 #include "llappviewer.h"
 #include "llviewercontrol.h"
+#include "llviewercamera.h"
+#include "llsdserialize.h"
+#include "llsdutil_math.h"
 
 LLAgentPilot gAgentPilot;
 
-BOOL LLAgentPilot::sLoop = TRUE;
-BOOL LLAgentPilot::sReplaySession = FALSE;
-
 LLAgentPilot::LLAgentPilot() :
 	mNumRuns(-1),
 	mQuitAfterRuns(FALSE),
@@ -47,7 +47,10 @@ LLAgentPilot::LLAgentPilot() :
 	mLastRecordTime(0.f),
 	mStarted(FALSE),
 	mPlaying(FALSE),
-	mCurrentAction(0)
+	mCurrentAction(0),
+	mOverrideCamera(FALSE),
+	mLoop(TRUE),
+	mReplaySession(FALSE)
 {
 }
 
@@ -55,7 +58,26 @@ LLAgentPilot::~LLAgentPilot()
 {
 }
 
-void LLAgentPilot::load(const std::string& filename)
+void LLAgentPilot::load()
+{
+	std::string txt_filename = gSavedSettings.getString("StatsPilotFile");
+	std::string xml_filename = gSavedSettings.getString("StatsPilotXMLFile");
+	if (LLFile::isfile(xml_filename))
+	{
+		loadXML(xml_filename);
+	}
+	else if (LLFile::isfile(txt_filename))
+	{
+		loadTxt(txt_filename);
+	}
+	else
+	{
+		lldebugs << "no autopilot file found" << llendl;
+		return;
+	}
+}
+
+void LLAgentPilot::loadTxt(const std::string& filename)
 {
 	if(filename.empty())
 	{
@@ -75,6 +97,7 @@ void LLAgentPilot::load(const std::string& filename)
 		llinfos << "Opening pilot file " << filename << llendl;
 	}
 
+	mActions.reset();
 	S32 num_actions;
 
 	file >> num_actions;
@@ -89,10 +112,59 @@ void LLAgentPilot::load(const std::string& filename)
 		mActions.put(new_action);
 	}
 
+	mOverrideCamera = false;
+	
+	file.close();
+}
+
+void LLAgentPilot::loadXML(const std::string& filename)
+{
+	if(filename.empty())
+	{
+		return;
+	}
+	
+	llifstream file(filename);
+
+	if (!file)
+	{
+		lldebugs << "Couldn't open " << filename
+			<< ", aborting agentpilot load!" << llendl;
+		return;
+	}
+	else
+	{
+		llinfos << "Opening pilot file " << filename << llendl;
+	}
+
+	mActions.reset();
+	LLSD record;
+	while (!file.eof() && LLSDSerialize::fromXML(record, file))
+	{
+		Action action;
+		action.mTime = record["time"].asReal();
+		action.mType = (EActionType)record["type"].asInteger();
+		action.mCameraView = record["camera_view"].asReal();
+		action.mTarget = ll_vector3d_from_sd(record["target"]);
+		action.mCameraOrigin = ll_vector3_from_sd(record["camera_origin"]);
+		action.mCameraXAxis = ll_vector3_from_sd(record["camera_xaxis"]);
+		action.mCameraYAxis = ll_vector3_from_sd(record["camera_yaxis"]);
+		action.mCameraZAxis = ll_vector3_from_sd(record["camera_zaxis"]);
+		mActions.put(action);
+	}
+	mOverrideCamera = true;
 	file.close();
 }
 
-void LLAgentPilot::save(const std::string& filename)
+void LLAgentPilot::save()
+{
+	std::string txt_filename = gSavedSettings.getString("StatsPilotFile");
+	std::string xml_filename = gSavedSettings.getString("StatsPilotXMLFile");
+	saveTxt(txt_filename);
+	saveXML(xml_filename);
+}
+
+void LLAgentPilot::saveTxt(const std::string& filename)
 {
 	llofstream file;
 	file.open(filename);
@@ -108,12 +180,41 @@ void LLAgentPilot::save(const std::string& filename)
 	for (i = 0; i < mActions.count(); i++)
 	{
 		file << mActions[i].mTime << "\t" << mActions[i].mType << "\t";
-		file << std::setprecision(32) << mActions[i].mTarget.mdV[VX] << "\t" << mActions[i].mTarget.mdV[VY] << "\t" << mActions[i].mTarget.mdV[VZ] << '\n';
+		file << std::setprecision(32) << mActions[i].mTarget.mdV[VX] << "\t" << mActions[i].mTarget.mdV[VY] << "\t" << mActions[i].mTarget.mdV[VZ];
+		file << '\n';
 	}
 
 	file.close();
 }
 
+void LLAgentPilot::saveXML(const std::string& filename)
+{
+	llofstream file;
+	file.open(filename);
+
+	if (!file)
+	{
+		llinfos << "Couldn't open " << filename << ", aborting agentpilot save!" << llendl;
+	}
+
+	S32 i;
+	for (i = 0; i < mActions.count(); i++)
+	{
+		Action& action = mActions[i];
+		LLSD record;
+		record["time"] = (LLSD::Real)action.mTime;
+		record["type"] = (LLSD::Integer)action.mType;
+		record["camera_view"] = (LLSD::Real)action.mCameraView;
+		record["target"] = ll_sd_from_vector3d(action.mTarget);
+		record["camera_origin"] = ll_sd_from_vector3(action.mCameraOrigin);
+		record["camera_xaxis"] = ll_sd_from_vector3(action.mCameraXAxis);
+		record["camera_yaxis"] = ll_sd_from_vector3(action.mCameraYAxis);
+		record["camera_zaxis"] = ll_sd_from_vector3(action.mCameraZAxis);
+		LLSDSerialize::toXML(record, file);
+	}
+	file.close();
+}
+
 void LLAgentPilot::startRecord()
 {
 	mActions.reset();
@@ -125,7 +226,7 @@ void LLAgentPilot::startRecord()
 void LLAgentPilot::stopRecord()
 {
 	gAgentPilot.addAction(STRAIGHT);
-	gAgentPilot.save(gSavedSettings.getString("StatsPilotFile"));
+	gAgentPilot.save();
 	mRecording = FALSE;
 }
 
@@ -136,6 +237,12 @@ void LLAgentPilot::addAction(enum EActionType action_type)
 	action.mType = action_type;
 	action.mTarget = gAgent.getPositionGlobal();
 	action.mTime = mTimer.getElapsedTimeF32();
+	LLViewerCamera *cam = LLViewerCamera::getInstance();
+	action.mCameraView = cam->getView();
+	action.mCameraOrigin = cam->getOrigin();
+	action.mCameraXAxis = cam->getXAxis();
+	action.mCameraYAxis = cam->getYAxis();
+	action.mCameraZAxis = cam->getZAxis();
 	mLastRecordTime = (F32)action.mTime;
 	mActions.put(action);
 }
@@ -152,6 +259,7 @@ void LLAgentPilot::startPlayback()
 		{
 			llinfos << "Starting playback, moving to waypoint 0" << llendl;
 			gAgent.startAutoPilotGlobal(mActions[0].mTarget);
+			moveCamera();
 			mStarted = FALSE;
 		}
 		else
@@ -172,12 +280,53 @@ void LLAgentPilot::stopPlayback()
 		gAgent.stopAutoPilot();
 	}
 
-	if (sReplaySession)
+	if (mReplaySession)
 	{
 		LLAppViewer::instance()->forceQuit();
 	}
 }
 
+void LLAgentPilot::moveCamera()
+{
+	if (!getOverrideCamera())
+		return;
+
+	if (mCurrentAction<mActions.count())
+	{
+		S32 start_index = llmax(mCurrentAction-1,0);
+		S32 end_index = mCurrentAction;
+		F32 t = 0.0;
+		F32 timedelta = mActions[end_index].mTime - mActions[start_index].mTime;
+		F32 tickelapsed = mTimer.getElapsedTimeF32()-mActions[start_index].mTime;
+		if (timedelta > 0.0)
+		{
+			t = tickelapsed/timedelta;
+		}
+
+		if ((t<0.0)||(t>1.0))
+		{
+			llwarns << "mCurrentAction is invalid, t = " << t << llendl;
+			return;
+		}
+		
+		Action& start = mActions[start_index];
+		Action& end = mActions[end_index];
+
+		F32 view = lerp(start.mCameraView, end.mCameraView, t);
+		LLVector3 origin = lerp(start.mCameraOrigin, end.mCameraOrigin, t);
+		LLQuaternion start_quat(start.mCameraXAxis, start.mCameraYAxis, start.mCameraZAxis);
+		LLQuaternion end_quat(end.mCameraXAxis, end.mCameraYAxis, end.mCameraZAxis);
+		LLQuaternion quat = nlerp(t, start_quat, end_quat);
+		LLMatrix3 mat(quat);
+	
+		LLViewerCamera::getInstance()->setView(view);
+		LLViewerCamera::getInstance()->setOrigin(origin);
+		LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]);
+		LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]);
+		LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]);
+	}
+}
+
 void LLAgentPilot::updateTarget()
 {
 	if (mPlaying)
@@ -209,12 +358,13 @@ void LLAgentPilot::updateTarget()
 				if (mCurrentAction < mActions.count())
 				{
 					gAgent.startAutoPilotGlobal(mActions[mCurrentAction].mTarget);
+					moveCamera();
 				}
 				else
 				{
 					stopPlayback();
 					mNumRuns--;
-					if (sLoop)
+					if (mLoop)
 					{
 						if ((mNumRuns < 0) || (mNumRuns > 0))
 						{
@@ -249,29 +399,8 @@ void LLAgentPilot::updateTarget()
 	}
 }
 
-// static
-void LLAgentPilot::startRecord(void *)
-{
-	gAgentPilot.startRecord();
-}
-
-void LLAgentPilot::saveRecord(void *)
+void LLAgentPilot::addWaypoint()
 {
-	gAgentPilot.stopRecord();
-}
-
-void LLAgentPilot::addWaypoint(void *)
-{
-	gAgentPilot.addAction(STRAIGHT);
-}
-
-void LLAgentPilot::startPlayback(void *)
-{
-	gAgentPilot.mNumRuns = -1;
-	gAgentPilot.startPlayback();
+	addAction(STRAIGHT);
 }
 
-void LLAgentPilot::stopPlayback(void *)
-{
-	gAgentPilot.stopPlayback();
-}
diff --git a/indra/newview/llagentpilot.h b/indra/newview/llagentpilot.h
index f3d34246ae77a728ce9b409e5083be6eb6fd820a..dd1709ec0c21cb1af0d562a96d865c21c0a8c1d3 100644
--- a/indra/newview/llagentpilot.h
+++ b/indra/newview/llagentpilot.h
@@ -46,8 +46,12 @@ class LLAgentPilot
 	LLAgentPilot();
 	virtual ~LLAgentPilot();
 
-	void load(const std::string& filename);
-	void save(const std::string& filename);
+	void load();
+	void loadTxt(const std::string& filename);
+	void loadXML(const std::string& filename);
+	void save();
+	void saveTxt(const std::string& filename);
+	void saveXML(const std::string& filename);
 
 	void startRecord();
 	void stopRecord();
@@ -56,19 +60,34 @@ class LLAgentPilot
 	void startPlayback();
 	void stopPlayback();
 
+	bool isRecording() { return mRecording; }
+	bool isPlaying() { return mPlaying; }
+	bool getOverrideCamera() { return mOverrideCamera; }
+	
 	void updateTarget();
 
-	static void startRecord(void *);
-	static void addWaypoint(void *);
-	static void saveRecord(void *);
-	static void startPlayback(void *);
-	static void stopPlayback(void *);
-	static BOOL	sLoop;
-	static BOOL sReplaySession;
+	void addWaypoint();
+	void moveCamera();
+
+	void setReplaySession(BOOL new_val) { mReplaySession = new_val; }
+	BOOL getReplaySession() { return mReplaySession; }
+
+	void setLoop(BOOL new_val) { mLoop = new_val; }
+	BOOL getLoop() { return mLoop; }
+
+	void setQuitAfterRuns(BOOL quit_val) { mQuitAfterRuns = quit_val; }
+	void setNumRuns(S32 num_runs) { mNumRuns = num_runs; }
+	
+private:
+
+
+
+	BOOL	mLoop;
+	BOOL 	mReplaySession;
 
 	S32		mNumRuns;
 	BOOL	mQuitAfterRuns;
-private:
+
 	void setAutopilotTarget(const S32 id);
 
 	BOOL	mRecording;
@@ -78,6 +97,8 @@ class LLAgentPilot
 	BOOL	mPlaying;
 	S32		mCurrentAction;
 
+	BOOL	mOverrideCamera;
+
 	class Action
 	{
 	public:
@@ -85,10 +106,16 @@ class LLAgentPilot
 		EActionType		mType;
 		LLVector3d		mTarget;
 		F64				mTime;
+		F32				mCameraView;
+		LLVector3		mCameraOrigin;
+		LLVector3		mCameraXAxis;
+		LLVector3		mCameraYAxis;
+		LLVector3		mCameraZAxis;
 	};
 
 	LLDynamicArray<Action>	mActions;
 	LLTimer					mTimer;
+
 };
 
 extern LLAgentPilot gAgentPilot;
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index f9e850899ac68b450bb86faee71a5b9af3900388..1388d9aee010e87806b0efdccf6488be97cd2e1d 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -2773,75 +2773,6 @@ BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
 	*/
 }
 
-// Shim class to allow arbitrary boost::bind
-// expressions to be run as one-time idle callbacks.
-//
-// TODO: rework idle function spec to take a boost::function in the first place.
-class OnIdleCallbackOneTime
-{
-public:
-	OnIdleCallbackOneTime(nullary_func_t callable):
-		mCallable(callable)
-	{
-	}
-	static void onIdle(void *data)
-	{
-		gIdleCallbacks.deleteFunction(onIdle, data);
-		OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data);
-		self->call();
-		delete self;
-	}
-	void call()
-	{
-		mCallable();
-	}
-private:
-	nullary_func_t mCallable;
-};
-
-void doOnIdleOneTime(nullary_func_t callable)
-{
-	OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable);
-	gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor);
-}
-
-// Shim class to allow generic boost functions to be run as
-// recurring idle callbacks.  Callable should return true when done,
-// false to continue getting called.
-//
-// TODO: rework idle function spec to take a boost::function in the first place.
-class OnIdleCallbackRepeating
-{
-public:
-	OnIdleCallbackRepeating(bool_func_t callable):
-		mCallable(callable)
-	{
-	}
-	// Will keep getting called until the callable returns true.
-	static void onIdle(void *data)
-	{
-		OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data);
-		bool done = self->call();
-		if (done)
-		{
-			gIdleCallbacks.deleteFunction(onIdle, data);
-			delete self;
-		}
-	}
-	bool call()
-	{
-		return mCallable();
-	}
-private:
-	bool_func_t mCallable;
-};
-
-void doOnIdleRepeating(bool_func_t callable)
-{
-	OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable);
-	gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor);
-}
-
 class CallAfterCategoryFetchStage2: public LLInventoryFetchItemsObserver
 {
 public:
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index c65d9dc9eeda8448d3c3042fb336a14d2d095fa4..4b1d95cf2541bb3655b2c2ab500447b27fabc62e 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -248,15 +248,6 @@ class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
 
 LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);
 
-typedef boost::function<void ()> nullary_func_t;
-typedef boost::function<bool ()> bool_func_t;
-
-// Call a given callable once in idle loop.
-void doOnIdleOneTime(nullary_func_t callable);
-
-// Repeatedly call a callable in idle loop until it returns true.
-void doOnIdleRepeating(bool_func_t callable);
-
 // Invoke a given callable after category contents are fully fetched.
 void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb);
 
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 3a7e8dff64875d987716a0fb735e4ad5c86fa72e..746d2480b827a5bb50c721789593c760a9691c0c 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -46,6 +46,7 @@
 #include "llviewerstats.h"
 #include "llviewerstatsrecorder.h"
 #include "llmd5.h"
+#include "llmeshrepository.h"
 #include "llpumpio.h"
 #include "llmimetypes.h"
 #include "llslurl.h"
@@ -75,6 +76,8 @@
 #include "llteleporthistory.h"
 #include "lllocationhistory.h"
 #include "llfasttimerview.h"
+#include "llvector4a.h"
+#include "llviewermenufile.h"
 #include "llvoicechannel.h"
 #include "llvoavatarself.h"
 #include "llsidetray.h"
@@ -200,7 +203,6 @@
 // Include for security api initialization
 #include "llsecapi.h"
 #include "llmachineid.h"
-
 #include "llmainlooprepeater.h"
 
 // *FIX: These extern globals should be cleaned up.
@@ -306,7 +308,7 @@ BOOL gLogoutInProgress = FALSE;
 
 ////////////////////////////////////////////////////////////
 // Internal globals... that should be removed.
-static std::string gArgs;
+static std::string gArgs = "Mesh Beta";
 
 const std::string MARKER_FILE_NAME("SecondLife.exec_marker");
 const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");
@@ -506,8 +508,8 @@ static void settings_to_globals()
 	LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections");
 	LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius");
 
-	gAgentPilot.mNumRuns		= gSavedSettings.getS32("StatsNumRuns");
-	gAgentPilot.mQuitAfterRuns	= gSavedSettings.getBOOL("StatsQuitAfterRuns");
+	gAgentPilot.setNumRuns(gSavedSettings.getS32("StatsNumRuns"));
+	gAgentPilot.setQuitAfterRuns(gSavedSettings.getBOOL("StatsQuitAfterRuns"));
 	gAgent.setHideGroupTitle(gSavedSettings.getBOOL("RenderHideGroupTitle"));
 
 	gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
@@ -517,7 +519,8 @@ static void settings_to_globals()
 
 static void settings_modify()
 {
-	LLRenderTarget::sUseFBO				= gSavedSettings.getBOOL("RenderUseFBO");
+	LLRenderTarget::sUseFBO				= gSavedSettings.getBOOL("RenderDeferred");
+	LLPipeline::sRenderDeferred			= gSavedSettings.getBOOL("RenderDeferred");
 	LLVOAvatar::sUseImpostors			= gSavedSettings.getBOOL("RenderUseImpostors");
 	LLVOSurfacePatch::sLODFactor		= gSavedSettings.getF32("RenderTerrainLODFactor");
 	LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4]
@@ -568,7 +571,7 @@ class LLFastTimerLogThread : public LLThread
 	std::string mFile;
 
 	LLFastTimerLogThread(std::string& test_name) : LLThread("fast timer log")
-	{
+ 	{
 		std::string file_name = test_name + std::string(".slp");
 		mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name);
 	}
@@ -586,7 +589,6 @@ class LLFastTimerLogThread : public LLThread
 
 		os.close();
 	}
-
 };
 
 //virtual
@@ -672,6 +674,9 @@ bool LLAppViewer::init()
 	//
 	LLFastTimer::reset();
 
+	// initialize SSE options
+	LLVector4a::initClass();
+
 	// Need to do this initialization before we do anything else, since anything
 	// that touches files should really go through the lldir API
 	gDirUtilp->initAppDirs("SecondLife");
@@ -888,7 +893,7 @@ bool LLAppViewer::init()
 	
 	// Initialize the repeater service.
 	LLMainLoopRepeater::instance().start();
-	
+
 	//
 	// Initialize the window
 	//
@@ -935,6 +940,18 @@ bool LLAppViewer::init()
 		return 0;
 	}
 
+	// Without SSE2 support we will crash almost immediately, warn here.
+	if (!gSysCPU.hasSSE2())
+	{	
+		// can't use an alert here since we're exiting and
+		// all hell breaks lose.
+		OSMessageBox(
+			LLNotifications::instance().getGlobalString("UnsupportedCPUSSE2"),
+			LLStringUtil::null,
+			OSMB_OK);
+		return 0;
+	}
+
 	// alert the user if they are using unsupported hardware
 	if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware"))
 	{
@@ -994,7 +1011,7 @@ bool LLAppViewer::init()
 	gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString();
 
 	// Save the current version to the prefs file
-	gSavedSettings.setString("LastRunVersion", 
+	gSavedSettings.setString("LastRunVersion",
 							 LLVersionInfo::getChannelAndVersion());
 
 	gSimLastTime = gRenderStartTime.getElapsedTimeF32();
@@ -1072,7 +1089,7 @@ bool LLAppViewer::mainLoop()
 	gServicePump = new LLPumpIO(gAPRPoolp);
 	LLHTTPClient::setPump(*gServicePump);
 	LLCurl::setCAFile(gDirUtilp->getCAFile());
-	
+
 	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
 
 	LLVoiceChannel::initClass();
@@ -1316,6 +1333,7 @@ bool LLAppViewer::mainLoop()
 						break;
 					}
 				}
+				gMeshRepo.update() ;
 
 				if(!total_work_pending) //pause texture fetching threads if nothing to process.
 				{
@@ -1421,6 +1439,20 @@ bool LLAppViewer::cleanup()
 	// workaround for DEV-35406 crash on shutdown
 	LLEventPumps::instance().reset();
 
+	if (LLFastTimerView::sAnalyzePerformance)
+	{
+		llinfos << "Analyzing performance" << llendl;
+		std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp";
+		std::string current_name  = LLFastTimer::sLogName + ".slp"; 
+		std::string report_name   = LLFastTimer::sLogName + "_report.csv";
+
+		LLFastTimerView::doAnalysis(
+			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name),
+			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name),
+			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name));		
+	}
+	LLMetricPerformanceTesterBasic::cleanClass();
+
 	// remove any old breakpad minidump files from the log directory
 	if (! isError())
 	{
@@ -1459,6 +1491,9 @@ bool LLAppViewer::cleanup()
 
 	llinfos << "Cleaning Up" << llendflush;
 
+	// shut down mesh streamer
+	gMeshRepo.shutdown();
+
 	// Must clean up texture references before viewer window is destroyed.
 	if(LLHUDManager::instanceExists())
 	{
@@ -1727,6 +1762,8 @@ bool LLAppViewer::cleanup()
 	sTextureFetch->shutDownTextureCacheThread() ;
 	sTextureFetch->shutDownImageDecodeThread() ;
 
+	LLFilePickerThread::cleanupClass();
+
 	delete sTextureCache;
     sTextureCache = NULL;
 	delete sTextureFetch;
@@ -1748,7 +1785,8 @@ bool LLAppViewer::cleanup()
 			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name),
 			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name),
 			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name));
-	}
+	}	
+
 	LLMetricPerformanceTesterBasic::cleanClass() ;
 
 #if LL_RECORD_VIEWER_STATS
@@ -1876,6 +1914,11 @@ bool LLAppViewer::initThreads()
 		mFastTimerLogThread->start();
 	}
 
+	// Mesh streaming and caching
+	gMeshRepo.init();
+
+	LLFilePickerThread::initClass();
+
 	// *FIX: no error handling here!
 	return true;
 }
@@ -2088,6 +2131,8 @@ bool LLAppViewer::initConfiguration()
 	gSavedSettings.setString("ClientSettingsFile", 
         gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
 
+	gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel());
+
 #ifndef	LL_RELEASE_FOR_DOWNLOAD
 	// provide developer build only overrides for these control variables that are not
 	// persisted to settings.xml
@@ -2258,7 +2303,6 @@ bool LLAppViewer::initConfiguration()
     {
 		LLVersionInfo::resetChannel(clp.getOption("channel")[0]);
 	}
-	
 
 	// If we have specified crash on startup, set the global so we'll trigger the crash at the right time
 	if(clp.hasOption("crashonstartup"))
@@ -2269,12 +2313,12 @@ bool LLAppViewer::initConfiguration()
 	if (clp.hasOption("logperformance"))
 	{
 		LLFastTimer::sLog = TRUE;
-		LLFastTimer::sLogName = std::string("performance");
+		LLFastTimer::sLogName = std::string("performance");		
 	}
 	
 	if (clp.hasOption("logmetrics"))
-	{
-		LLFastTimer::sMetricLog = TRUE ;
+ 	{
+ 		LLFastTimer::sMetricLog = TRUE ;
 		// '--logmetrics' can be specified with a named test metric argument so the data gathering is done only on that test
 		// In the absence of argument, every metric is gathered (makes for a rather slow run and hard to decipher report...)
 		std::string test_name = clp.getOption("logmetrics")[0];
@@ -2288,7 +2332,7 @@ bool LLAppViewer::initConfiguration()
 		{
 			LLFastTimer::sLogName = test_name;
 		}
-	}
+ 	}
 
 	if (clp.hasOption("graphicslevel"))
 	{
@@ -2331,7 +2375,7 @@ bool LLAppViewer::initConfiguration()
 
 	if (clp.hasOption("replaysession"))
 	{
-		LLAgentPilot::sReplaySession = TRUE;
+		gAgentPilot.setReplaySession(TRUE);
 	}
 
 	if (clp.hasOption("nonotifications"))
@@ -2538,7 +2582,7 @@ bool LLAppViewer::initConfiguration()
 
 namespace {
     // *TODO - decide if there's a better place for these functions.
-    // do we need a file llupdaterui.cpp or something? -brad
+	// do we need a file llupdaterui.cpp or something? -brad
 
 	void apply_update_callback(LLSD const & notification, LLSD const & response)
 	{
@@ -2631,8 +2675,8 @@ namespace {
 		LLAppViewer::instance()->forceQuit();
 	}
 	
-    bool notify_update(LLSD const & evt)
-    {
+	bool notify_update(LLSD const & evt)
+	{
 		std::string notification_name;
 		switch (evt["type"].asInteger())
 		{
@@ -2651,8 +2695,8 @@ namespace {
 		}
 
 		// let others also handle this event by default
-        return false;
-    }
+		return false;
+	}
 	
 	bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt)
 	{
@@ -2809,6 +2853,7 @@ bool LLAppViewer::initWindow()
 	gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
 
 	gPipeline.init();
+		
 	stop_glerror();
 	gViewerWindow->initGLDefaults();
 
@@ -2883,7 +2928,7 @@ void LLAppViewer::cleanupSavedSettings()
 		if (!maximized)
 		{
 			LLCoordScreen window_pos;
-
+			
 			if (gViewerWindow->mWindow->getPosition(&window_pos))
 			{
 				gSavedSettings.setS32("WindowX", window_pos.mX);
@@ -2973,6 +3018,8 @@ void LLAppViewer::writeSystemInfo()
 	LL_INFOS("SystemInfo") << "OS: " << getOSInfo().getOSStringSimple() << LL_ENDL;
 	LL_INFOS("SystemInfo") << "OS info: " << getOSInfo() << LL_ENDL;
 
+	LL_INFOS("SystemInfo") << "Timers: " << LLFastTimer::sClockType << LL_ENDL;
+
 	writeDebugInfo(); // Save out debug_info.log early, in case of crash.
 }
 
@@ -3945,6 +3992,8 @@ static LLFastTimer::DeclareTimer FTM_OBJECTLIST_UPDATE("Update Objectlist");
 static LLFastTimer::DeclareTimer FTM_REGION_UPDATE("Update Region");
 static LLFastTimer::DeclareTimer FTM_WORLD_UPDATE("Update World");
 static LLFastTimer::DeclareTimer FTM_NETWORK("Network");
+static LLFastTimer::DeclareTimer FTM_AGENT_NETWORK("Agent Network");
+static LLFastTimer::DeclareTimer FTM_VLMANAGER("VL Manager");
 
 ///////////////////////////////////////////////////////
 // idle()
@@ -3965,6 +4014,8 @@ void LLAppViewer::idle()
 	LLEventTimer::updateClass();
 	LLCriticalDamp::updateInterpolants();
 	LLMortician::updateClass();
+	LLFilePickerThread::clearDead();  //calls LLFilePickerThread::notify()
+
 	F32 dt_raw = idle_timer.getElapsedTimeAndResetF32();
 
 	// Cap out-of-control frame times
@@ -4300,8 +4351,12 @@ void LLAppViewer::idle()
 
 	LLWorld::getInstance()->updateParticles();
 
-	if (LLViewerJoystick::getInstance()->getOverrideCamera())
+	if (gAgentPilot.isPlaying() && gAgentPilot.getOverrideCamera())
 	{
+		gAgentPilot.moveCamera();
+	}
+	else if (LLViewerJoystick::getInstance()->getOverrideCamera())
+	{ 
 		LLViewerJoystick::getInstance()->moveFlycam();
 	}
 	else
@@ -4539,6 +4594,11 @@ static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;
 #endif
 
 static LLFastTimer::DeclareTimer FTM_IDLE_NETWORK("Idle Network");
+static LLFastTimer::DeclareTimer FTM_MESSAGE_ACKS("Message Acks");
+static LLFastTimer::DeclareTimer FTM_RETRANSMIT("Retransmit");
+static LLFastTimer::DeclareTimer FTM_TIMEOUT_CHECK("Timeout Check");
+static LLFastTimer::DeclareTimer FTM_DYNAMIC_THROTTLE("Dynamic Throttle");
+static LLFastTimer::DeclareTimer FTM_CHECK_REGION_CIRCUIT("Check Region Circuit");
 
 void LLAppViewer::idleNetwork()
 {
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index d328567a0e0f4f2e7e919fffebdaad9618af947a..6396ca91ffa6521c4fb597ed6c439d6d23ba52a4 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -26,12 +26,6 @@
 
 #include "llviewerprecompiledheaders.h"
 
-#if defined(_DEBUG)
-# if _MSC_VER >= 1400 // Visual C++ 2005 or later
-#	define WINDOWS_CRT_MEM_CHECKS 1
-# endif
-#endif
-
 #include "llappviewerwin32.h"
 
 #include "llmemtype.h"
@@ -440,7 +434,11 @@ bool LLAppViewerWin32::initHardwareTest()
 		LL_WARNS("AppInit") << " Someone took over my exception handler (post hardware probe)!" << LL_ENDL;
 	}
 
-	gGLManager.mVRAM = gDXHardware.getVRAM();
+	if (gGLManager.mVRAM == 0)
+	{
+		gGLManager.mVRAM = gDXHardware.getVRAM();
+	}
+
 	LL_INFOS("AppInit") << "Detected VRAM: " << gGLManager.mVRAM << LL_ENDL;
 
 	return true;
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
index dd5bc74b2a7a2154b09d838c90850a1d162e40c6..c08771c5e7d2352049fa4c56004e6432d182d272 100644
--- a/indra/newview/llassetuploadresponders.cpp
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -60,6 +60,7 @@
 #include "llnotificationsutil.h"
 #include "llscrolllistctrl.h"
 #include "llsdserialize.h"
+#include "llsdutil.h"
 #include "llvfs.h"
 
 // When uploading multiple files, don't display any of them when uploading more than this number.
@@ -67,6 +68,106 @@ static const S32 FILE_COUNT_DISPLAY_THRESHOLD = 5;
 
 void dialog_refresh_all();
 
+void on_new_single_inventory_upload_complete(
+	LLAssetType::EType asset_type,
+	LLInventoryType::EType inventory_type,
+	const std::string inventory_type_string,
+	const LLUUID& item_folder_id,
+	const std::string& item_name,
+	const std::string& item_description,
+	const LLSD& server_response,
+	S32 upload_price)
+{
+	if ( upload_price > 0 )
+	{
+		// this upload costed us L$, update our balance
+		// and display something saying that it cost L$
+		LLStatusBar::sendMoneyBalanceRequest();
+
+		LLSD args;
+		args["AMOUNT"] = llformat("%d", upload_price);
+		LLNotificationsUtil::add("UploadPayment", args);
+	}
+
+	if( item_folder_id.notNull() )
+	{
+		U32 everyone_perms = PERM_NONE;
+		U32 group_perms = PERM_NONE;
+		U32 next_owner_perms = PERM_ALL;
+		if( server_response.has("new_next_owner_mask") )
+		{
+			// The server provided creation perms so use them.
+			// Do not assume we got the perms we asked for in
+			// since the server may not have granted them all.
+			everyone_perms = server_response["new_everyone_mask"].asInteger();
+			group_perms = server_response["new_group_mask"].asInteger();
+			next_owner_perms = server_response["new_next_owner_mask"].asInteger();
+		}
+		else 
+		{
+			// The server doesn't provide creation perms
+			// so use old assumption-based perms.
+			if( inventory_type_string != "snapshot")
+			{
+				next_owner_perms = PERM_MOVE | PERM_TRANSFER;
+			}
+		}
+
+		LLPermissions new_perms;
+		new_perms.init(
+			gAgent.getID(),
+			gAgent.getID(),
+			LLUUID::null,
+			LLUUID::null);
+
+		new_perms.initMasks(
+			PERM_ALL,
+			PERM_ALL,
+			everyone_perms,
+			group_perms,
+			next_owner_perms);
+
+		S32 creation_date_now = time_corrected();
+		LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem(
+			server_response["new_inventory_item"].asUUID(),
+			item_folder_id,
+			new_perms,
+			server_response["new_asset"].asUUID(),
+			asset_type,
+			inventory_type,
+			item_name,
+			item_description,
+			LLSaleInfo::DEFAULT,
+			LLInventoryItemFlags::II_FLAGS_NONE,
+			creation_date_now);
+
+		gInventory.updateItem(item);
+		gInventory.notifyObservers();
+
+		// Show the preview panel for textures and sounds to let
+		// user know that the image (or snapshot) arrived intact.
+		LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel();
+		if ( panel )
+		{
+			LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
+
+			panel->setSelection(
+				server_response["new_inventory_item"].asUUID(),
+				TAKE_FOCUS_NO);
+
+			// restore keyboard focus
+			gFocusMgr.setKeyboardFocus(focus);
+		}
+	}
+	else
+	{
+		llwarns << "Can't find a folder to put it in" << llendl;
+	}
+
+	// remove the "Uploading..." message
+	LLUploadDialog::modalUploadFinished();	
+}
+
 LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
 											   const LLUUID& vfile_id,
 											   LLAssetType::EType asset_type)
@@ -84,9 +185,10 @@ LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
 	}
 }
 
-LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
-											   const std::string& file_name, 
-											   LLAssetType::EType asset_type)
+LLAssetUploadResponder::LLAssetUploadResponder(
+	const LLSD &post_data,
+	const std::string& file_name, 
+	LLAssetType::EType asset_type)
 	: LLHTTPClient::Responder(),
 	  mPostData(post_data),
 	  mFileName(file_name),
@@ -135,6 +237,7 @@ void LLAssetUploadResponder::result(const LLSD& content)
 	lldebugs << "LLAssetUploadResponder::result from capabilities" << llendl;
 
 	std::string state = content["state"];
+
 	if (state == "upload")
 	{
 		uploadUpload(content);
@@ -196,16 +299,36 @@ void LLAssetUploadResponder::uploadComplete(const LLSD& content)
 {
 }
 
-LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(const LLSD& post_data,
-														   const LLUUID& vfile_id,
-														   LLAssetType::EType asset_type)
-: LLAssetUploadResponder(post_data, vfile_id, asset_type)
+LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(
+	const LLSD& post_data,
+	const LLUUID& vfile_id,
+	LLAssetType::EType asset_type)
+	: LLAssetUploadResponder(post_data, vfile_id, asset_type)
 {
 }
 
-LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(const LLSD& post_data, const std::string& file_name, LLAssetType::EType asset_type)
-: LLAssetUploadResponder(post_data, file_name, asset_type)
+LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(
+	const LLSD& post_data,
+	const std::string& file_name,
+	LLAssetType::EType asset_type)
+	: LLAssetUploadResponder(post_data, file_name, asset_type)
+{
+}
+
+// virtual
+void LLNewAgentInventoryResponder::error(U32 statusNum, const std::string& reason)
 {
+	LLAssetUploadResponder::error(statusNum, reason);
+	//LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, LLUUID(), FALSE);
+}
+
+
+//virtual 
+void LLNewAgentInventoryResponder::uploadFailure(const LLSD& content)
+{
+	LLAssetUploadResponder::uploadFailure(content);
+
+	//LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], FALSE);
 }
 
 //virtual 
@@ -219,95 +342,31 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)
 
 	LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString());
 	LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString());
-	S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+	S32 expected_upload_cost = 0;
 
 	// Update L$ and ownership credit information
 	// since it probably changed on the server
 	if (asset_type == LLAssetType::AT_TEXTURE ||
 		asset_type == LLAssetType::AT_SOUND ||
-		asset_type == LLAssetType::AT_ANIMATION)
+		asset_type == LLAssetType::AT_ANIMATION ||
+		asset_type == LLAssetType::AT_MESH)
 	{
-		LLStatusBar::sendMoneyBalanceRequest();
-
-		LLSD args;
-		args["AMOUNT"] = llformat("%d", expected_upload_cost);
-		LLNotificationsUtil::add("UploadPayment", args);
+		expected_upload_cost = 
+			LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
 	}
 
-	// Actually add the upload to viewer inventory
-	llinfos << "Adding " << content["new_inventory_item"].asUUID() << " "
-			<< content["new_asset"].asUUID() << " to inventory." << llendl;
-	if(mPostData["folder_id"].asUUID().notNull())
-	{
-		//std::ostringstream out;
-		//LLSDXMLFormatter *formatter = new LLSDXMLFormatter;
-		//formatter->format(mPostData, out, LLSDFormatter::OPTIONS_PRETTY);
-		//llinfos << "Post Data: " << out.str() << llendl;
+	on_new_single_inventory_upload_complete(
+		asset_type,
+		inventory_type,
+		mPostData["asset_type"].asString(),
+		mPostData["folder_id"].asUUID(),
+		mPostData["name"],
+		mPostData["description"],
+		content,
+		expected_upload_cost);
 
-		U32 everyone_perms = PERM_NONE;
-		U32 group_perms = PERM_NONE;
-		U32 next_owner_perms = PERM_ALL;
-		if(content.has("new_next_owner_mask"))
-		{
-			// This is a new sim that provides creation perms so use them.
-			// Do not assume we got the perms we asked for in mPostData 
-			// since the sim may not have granted them all.
-			everyone_perms = content["new_everyone_mask"].asInteger();
-			group_perms = content["new_group_mask"].asInteger();
-			next_owner_perms = content["new_next_owner_mask"].asInteger();
-		}
-		else 
-		{
-			// This old sim doesn't provide creation perms so use old assumption-based perms.
-			if(mPostData["inventory_type"].asString() != "snapshot")
-			{
-				next_owner_perms = PERM_MOVE | PERM_TRANSFER;
-			}
-		}
-		LLPermissions new_perms;
-		new_perms.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
-		new_perms.initMasks(PERM_ALL, PERM_ALL, everyone_perms, group_perms, next_owner_perms);
-		S32 creation_date_now = time_corrected();
-		LLPointer<LLViewerInventoryItem> item
-			= new LLViewerInventoryItem(content["new_inventory_item"].asUUID(),
-										mPostData["folder_id"].asUUID(),
-										new_perms,
-										content["new_asset"].asUUID(),
-										asset_type,
-										inventory_type,
-										mPostData["name"].asString(),
-										mPostData["description"].asString(),
-										LLSaleInfo::DEFAULT,
-										LLInventoryItemFlags::II_FLAGS_NONE,
-										creation_date_now);
-		gInventory.updateItem(item);
-		gInventory.notifyObservers();
-
-		// Show the preview panel for textures and sounds to let
-		// user know that the image (or snapshot) arrived intact.
-		LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel();
-		if (active_panel)
-		{
-			active_panel->setSelection(content["new_inventory_item"].asUUID(), TAKE_FOCUS_NO);
-			if((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type)
-				&& LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD)
-			{
-				active_panel->openSelected();
-			}
-			//LLFloaterInventory::dumpSelectionInformation((void*)view);
-			// restore keyboard focus
-			LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
-			gFocusMgr.setKeyboardFocus(focus);
-		}
-	}
-	else
-	{
-		llwarns << "Can't find a folder to put it in" << llendl;
-	}
+	// continue uploading for bulk uploads
 
-	// remove the "Uploading..." message
-	LLUploadDialog::modalUploadFinished();
-	
 	// *FIX: This is a pretty big hack. What this does is check the
 	// file picker if there are any more pending uploads. If so,
 	// upload that file.
@@ -324,19 +383,42 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)
 
 		// Continuing the horrible hack above, we need to extract the originally requested permissions data, if any,
 		// and use them for each next file to be uploaded. Note the requested perms are not the same as the
-		// granted ones found in the given "content" structure but can still be found in mPostData. -MG
-		U32 everyone_perms   = mPostData.has("everyone_mask")   ? mPostData.get("everyone_mask"  ).asInteger() : PERM_NONE;
-		U32 group_perms      = mPostData.has("group_mask")      ? mPostData.get("group_mask"     ).asInteger() : PERM_NONE;
-		U32 next_owner_perms = mPostData.has("next_owner_mask") ? mPostData.get("next_owner_mask").asInteger() : PERM_NONE;
+		U32 everyone_perms =
+			content.has("everyone_mask") ?
+			content["everyone_mask"].asInteger() :
+			PERM_NONE;
+
+		U32 group_perms =
+			content.has("group_mask") ?
+			content["group_mask"].asInteger() :
+			PERM_NONE;
+
+		U32 next_owner_perms =
+			content.has("next_owner_mask") ?
+			content["next_owner_mask"].asInteger() :
+			PERM_NONE;
+
 		std::string display_name = LLStringUtil::null;
 		LLAssetStorage::LLStoreAssetCallback callback = NULL;
 		void *userdata = NULL;
-		upload_new_resource(next_file, asset_name, asset_name,
-				    0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
-				    next_owner_perms, group_perms,
-				    everyone_perms, display_name,
-				    callback, expected_upload_cost, userdata);
+
+		upload_new_resource(
+			next_file,
+			asset_name,
+			asset_name,
+			0,
+			LLFolderType::FT_NONE,
+			LLInventoryType::IT_NONE,
+			next_owner_perms,
+			group_perms,
+			everyone_perms,
+			display_name,
+			callback,
+			LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(),
+			userdata);
 	}
+
+	//LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], TRUE);
 }
 
 LLSendTexLayerResponder::LLSendTexLayerResponder(const LLSD& post_data,
@@ -390,17 +472,19 @@ void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason)
 	mBakedUploadData = NULL;	// deleted in onTextureUploadComplete()
 }
 
-LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data,
-																 const LLUUID& vfile_id,
-																 LLAssetType::EType asset_type)
-: LLAssetUploadResponder(post_data, vfile_id, asset_type)
+LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(
+	const LLSD& post_data,
+	const LLUUID& vfile_id,
+	LLAssetType::EType asset_type)
+	: LLAssetUploadResponder(post_data, vfile_id, asset_type)
 {
 }
 
-LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data,
-																 const std::string& file_name,
-																 LLAssetType::EType asset_type)
-: LLAssetUploadResponder(post_data, file_name, asset_type)
+LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(
+	const LLSD& post_data,
+	const std::string& file_name,
+	LLAssetType::EType asset_type)
+	: LLAssetUploadResponder(post_data, file_name, asset_type)
 {
 }
 
@@ -583,3 +667,474 @@ void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content)
 		break;
 	}
 }
+
+
+/////////////////////////////////////////////////////
+// LLNewAgentInventoryVariablePriceResponder::Impl //
+/////////////////////////////////////////////////////
+class LLNewAgentInventoryVariablePriceResponder::Impl
+{
+public:
+	Impl(
+		const LLUUID& vfile_id,
+		LLAssetType::EType asset_type,
+		const LLSD& inventory_data) :
+		mVFileID(vfile_id),
+		mAssetType(asset_type),
+		mInventoryData(inventory_data),
+		mFileName("")
+	{
+		if (!gVFS->getExists(vfile_id, asset_type))
+		{
+			llwarns
+				<< "LLAssetUploadResponder called with nonexistant "
+				<< "vfile_id " << vfile_id << llendl;
+			mVFileID.setNull();
+			mAssetType = LLAssetType::AT_NONE;
+		}
+	}
+
+	Impl(
+		const std::string& file_name,
+		LLAssetType::EType asset_type,
+		const LLSD& inventory_data) :
+		mFileName(file_name),
+		mAssetType(asset_type),
+		mInventoryData(inventory_data)
+	{
+		mVFileID.setNull();
+	}
+
+	std::string getFilenameOrIDString() const
+	{
+		return (mFileName.empty() ? mVFileID.asString() : mFileName);
+	}
+
+	LLUUID getVFileID() const
+	{
+		return mVFileID;
+	}
+
+	std::string getFilename() const
+	{
+		return mFileName;
+	}
+
+	LLAssetType::EType getAssetType() const
+	{
+		return mAssetType;
+	}
+
+	LLInventoryType::EType getInventoryType() const
+	{
+		return LLInventoryType::lookup(
+			mInventoryData["inventory_type"].asString());
+	}
+
+	std::string getInventoryTypeString() const
+	{
+		return mInventoryData["inventory_type"].asString();
+	}
+
+	LLUUID getFolderID() const
+	{
+		return mInventoryData["folder_id"].asUUID();
+	}
+
+	std::string getItemName() const
+	{
+		return mInventoryData["name"].asString();
+	}
+
+	std::string getItemDescription() const
+	{
+		return mInventoryData["description"].asString();
+	}
+
+	void displayCannotUploadReason(const std::string& reason)
+	{
+		LLSD args;
+		args["FILE"] = getFilenameOrIDString();
+		args["REASON"] = reason;
+
+
+		LLNotificationsUtil::add("CannotUploadReason", args);
+		LLUploadDialog::modalUploadFinished();
+	}
+
+	void onApplicationLevelError(const LLSD& error)
+	{
+		static const std::string _IDENTIFIER = "identifier";
+
+		static const std::string _INSUFFICIENT_FUNDS =
+			"NewAgentInventory_InsufficientLindenDollarBalance";
+		static const std::string _MISSING_REQUIRED_PARAMETER =
+			"NewAgentInventory_MissingRequiredParamater";
+		static const std::string _INVALID_REQUEST_BODY =
+			"NewAgentInventory_InvalidRequestBody";
+		static const std::string _RESOURCE_COST_DIFFERS =
+			"NewAgentInventory_ResourceCostDiffers";
+
+		static const std::string _MISSING_PARAMETER = "missing_parameter";
+		static const std::string _INVALID_PARAMETER = "invalid_parameter";
+		static const std::string _MISSING_RESOURCE = "missing_resource";
+		static const std::string _INVALID_RESOURCE = "invalid_resource";
+
+		// TODO* Add the other error_identifiers
+
+		std::string error_identifier = error[_IDENTIFIER].asString();
+
+		// TODO*: Pull these user visible strings from an xml file
+		// to be localized
+		if ( _INSUFFICIENT_FUNDS == error_identifier )
+		{
+			displayCannotUploadReason("You do not have a sufficient L$ balance to complete this upload.");
+		}
+		else if ( _MISSING_REQUIRED_PARAMETER == error_identifier )
+		{
+			// Missing parameters
+			if (error.has(_MISSING_PARAMETER) )
+			{
+				std::string message = 
+					"Upload request was missing required parameter '[P]'";
+				LLStringUtil::replaceString(
+					message,
+					"[P]",
+					error[_MISSING_PARAMETER].asString());
+
+				displayCannotUploadReason(message);
+			}
+			else
+			{
+				std::string message = 
+					"Upload request was missing a required parameter";
+				displayCannotUploadReason(message);					
+			}
+		}
+		else if ( _INVALID_REQUEST_BODY == error_identifier )
+		{
+			// Invalid request body, check to see if 
+			// a particular parameter was invalid
+			if ( error.has(_INVALID_PARAMETER) )
+			{
+				std::string message = "Upload parameter '[P]' is invalid.";
+				LLStringUtil::replaceString(
+					message,
+					"[P]",
+					error[_INVALID_PARAMETER].asString());
+
+				// See if the server also responds with what resource
+				// is missing.
+				if ( error.has(_MISSING_RESOURCE) )
+				{
+					message += "\nMissing resource '[R]'.";
+
+					LLStringUtil::replaceString(
+						message,
+						"[R]",
+						error[_MISSING_RESOURCE].asString());
+				}
+				else if ( error.has(_INVALID_RESOURCE) )
+				{
+					message += "\nInvalid resource '[R]'.";
+
+					LLStringUtil::replaceString(
+						message,
+						"[R]",
+						error[_INVALID_RESOURCE].asString());
+				}
+
+				displayCannotUploadReason(message);
+			}
+			else
+			{
+				std::string message = "Upload request was malformed";
+				displayCannotUploadReason(message);					
+			}
+		}
+		else if ( _RESOURCE_COST_DIFFERS == error_identifier )
+		{
+			displayCannotUploadReason("The resource cost associated with this upload is not consistent with the server.");
+		}
+		else
+		{
+			displayCannotUploadReason("Unknown Error");
+		}
+	}
+
+	void onTransportError()
+	{
+		displayCannotUploadReason(
+				"The server is experiencing unexpected difficulties.");
+	}
+
+	void onTransportError(const LLSD& error)
+	{
+		static const std::string _IDENTIFIER = "identifier";
+
+		static const std::string _SERVER_ERROR_AFTER_CHARGE =
+			"NewAgentInventory_ServerErrorAfterCharge";
+
+		std::string error_identifier = error[_IDENTIFIER].asString();
+
+		// TODO*: Pull the user visible strings from an xml file
+		// to be localized
+
+		if ( _SERVER_ERROR_AFTER_CHARGE == error_identifier )
+		{
+			displayCannotUploadReason(
+				"The server is experiencing unexpected difficulties.  You may have been charged for the upload.");
+		}
+		else
+		{
+			displayCannotUploadReason(
+				"The server is experiencing unexpected difficulties.");
+		}
+	}
+
+	bool uploadConfirmationCallback(
+		const LLSD& notification,
+		const LLSD& response,
+		boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder> responder)
+	{
+		S32 option;
+		std::string confirmation_url;
+
+		option = LLNotificationsUtil::getSelectedOption(
+			notification,
+			response);
+
+		confirmation_url =
+			notification["payload"]["confirmation_url"].asString();
+
+		// Yay!  We are confirming or cancelling our upload
+		switch(option)
+		{
+		case 0:
+		    {
+				confirmUpload(confirmation_url, responder);
+			}
+			break;
+		case 1:
+		default:
+			break;
+		}
+
+		return false;
+	}
+
+	void confirmUpload(
+		const std::string& confirmation_url,
+		boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder> responder)
+	{
+		if ( getFilename().empty() )
+		{
+			// we have no filename, use virtual file ID instead
+			LLHTTPClient::postFile(
+				confirmation_url,
+				getVFileID(),
+				getAssetType(),
+				responder);
+		}
+		else
+		{
+			LLHTTPClient::postFile(
+				confirmation_url,
+				getFilename(),
+				responder);
+		}
+	}
+
+
+private:
+	std::string mFileName;
+
+	LLSD mInventoryData;
+	LLAssetType::EType mAssetType;
+	LLUUID mVFileID;
+};
+
+///////////////////////////////////////////////
+// LLNewAgentInventoryVariablePriceResponder //
+///////////////////////////////////////////////
+LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder(
+	const LLUUID& vfile_id,
+	LLAssetType::EType asset_type,
+	const LLSD& inventory_info)
+{
+	mImpl = new Impl(
+		vfile_id,
+		asset_type,
+		inventory_info);
+}
+
+LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder(
+	const std::string& file_name,
+	LLAssetType::EType asset_type,
+	const LLSD& inventory_info)
+{
+	mImpl = new Impl(
+		file_name,
+		asset_type,
+		inventory_info);
+}
+
+LLNewAgentInventoryVariablePriceResponder::~LLNewAgentInventoryVariablePriceResponder()
+{
+	delete mImpl;
+}
+
+void LLNewAgentInventoryVariablePriceResponder::errorWithContent(
+	U32 statusNum,
+	const std::string& reason,
+	const LLSD& content)
+{
+	lldebugs 
+		<< "LLNewAgentInventoryVariablePrice::error " << statusNum 
+		<< " reason: " << reason << llendl;
+
+	if ( content.has("error") )
+	{
+		static const std::string _ERROR = "error";
+
+		mImpl->onTransportError(content[_ERROR]);
+	}
+	else
+	{
+		mImpl->onTransportError();
+	}
+}
+
+void LLNewAgentInventoryVariablePriceResponder::result(const LLSD& content)
+{
+	// Parse out application level errors and the appropriate
+	// responses for them
+	static const std::string _ERROR = "error";
+	static const std::string _STATE = "state";
+
+	static const std::string _COMPLETE = "complete";
+	static const std::string _CONFIRM_UPLOAD = "confirm_upload";
+
+	static const std::string _UPLOAD_PRICE = "upload_price";
+	static const std::string _RESOURCE_COST = "resource_cost";
+	static const std::string _RSVP = "rsvp";
+
+	// Check for application level errors
+	if ( content.has(_ERROR) )
+	{
+		onApplicationLevelError(content[_ERROR]);
+		return;
+	}
+
+	std::string state = content[_STATE];
+	LLAssetType::EType asset_type = mImpl->getAssetType();
+
+	if ( _COMPLETE == state )
+	{
+		// rename file in VFS with new asset id
+		if (mImpl->getFilename().empty())
+		{
+			// rename the file in the VFS to the actual asset id
+			// llinfos << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << llendl;
+			gVFS->renameFile(
+				mImpl->getVFileID(),
+				asset_type,
+				content["new_asset"].asUUID(),
+				asset_type);
+		}
+
+ 		on_new_single_inventory_upload_complete(
+ 			asset_type,
+ 			mImpl->getInventoryType(),
+			mImpl->getInventoryTypeString(),
+			mImpl->getFolderID(),
+			mImpl->getItemName(),
+			mImpl->getItemDescription(),
+			content,
+			content[_UPLOAD_PRICE].asInteger());
+
+		// TODO* Add bulk (serial) uploading or add
+		// a super class of this that does so
+	}
+	else if ( _CONFIRM_UPLOAD == state )
+	{
+		showConfirmationDialog(
+			content[_UPLOAD_PRICE].asInteger(),
+			content[_RESOURCE_COST].asInteger(),
+			content[_RSVP].asString());
+	}
+	else
+	{
+		onApplicationLevelError("");
+	}
+}
+
+void LLNewAgentInventoryVariablePriceResponder::onApplicationLevelError(
+	const LLSD& error)
+{
+	mImpl->onApplicationLevelError(error);
+}
+
+void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog(
+	S32 upload_price,
+	S32 resource_cost,
+	const std::string& confirmation_url)
+{
+	if ( 0 == upload_price )
+	{
+		// don't show confirmation dialog for free uploads, I mean,
+		// they're free!
+
+		// The creating of a new instrusive_ptr(this)
+		// creates a new boost::intrusive_ptr
+		// which is a copy of this.  This code is required because
+		// 'this' is always of type Class* and not the intrusive_ptr,
+		// and thus, a reference to 'this' is not registered
+		// by using just plain 'this'.
+
+		// Since LLNewAgentInventoryVariablePriceResponder is a
+		// reference counted class, it is possible (since the
+		// reference to a plain 'this' would be missed here) that,
+		// when using plain ol' 'this', that this object
+		// would be deleted before the callback is triggered
+		// and cause sadness.
+		mImpl->confirmUpload(
+			confirmation_url,
+			boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder>(this));
+	}
+	else
+	{
+		LLSD substitutions;
+		LLSD payload;
+
+		substitutions["PRICE"] = upload_price;
+
+		payload["confirmation_url"] = confirmation_url;
+
+		// The creating of a new instrusive_ptr(this)
+		// creates a new boost::intrusive_ptr
+		// which is a copy of this.  This code is required because
+		// 'this' is always of type Class* and not the intrusive_ptr,
+		// and thus, a reference to 'this' is not registered
+		// by using just plain 'this'.
+
+		// Since LLNewAgentInventoryVariablePriceResponder is a
+		// reference counted class, it is possible (since the
+		// reference to a plain 'this' would be missed here) that,
+		// when using plain ol' 'this', that this object
+		// would be deleted before the callback is triggered
+		// and cause sadness.
+		LLNotificationsUtil::add(
+			"UploadCostConfirmation",
+			substitutions,
+			payload,
+			boost::bind(
+				&LLNewAgentInventoryVariablePriceResponder::Impl::uploadConfirmationCallback,
+				mImpl,
+				_1,
+				_2,
+				boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder>(this)));
+	}
+}
+
+
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
index 752c0dfe456d02d8e3fcb9895e734a139042dcd7..70871b62e2f0fe4b53c455010dbd5a6854cb4ddc 100644
--- a/indra/newview/llassetuploadresponders.h
+++ b/indra/newview/llassetuploadresponders.h
@@ -55,15 +55,58 @@ class LLAssetUploadResponder : public LLHTTPClient::Responder
 	std::string mFileName;
 };
 
+// TODO*: Remove this once deprecated
 class LLNewAgentInventoryResponder : public LLAssetUploadResponder
 {
 public:
-	LLNewAgentInventoryResponder(const LLSD& post_data,
-								const LLUUID& vfile_id,
-								LLAssetType::EType asset_type);
-	LLNewAgentInventoryResponder(const LLSD& post_data, const std::string& file_name,
-											   LLAssetType::EType asset_type);
+	LLNewAgentInventoryResponder(
+		const LLSD& post_data,
+		const LLUUID& vfile_id,
+		LLAssetType::EType asset_type);
+	LLNewAgentInventoryResponder(
+		const LLSD& post_data,
+		const std::string& file_name,
+		LLAssetType::EType asset_type);
+    virtual void error(U32 statusNum, const std::string& reason);
 	virtual void uploadComplete(const LLSD& content);
+	virtual void uploadFailure(const LLSD& content);
+};
+
+// A base class which goes through and performs some default
+// actions for variable price uploads.  If more specific actions
+// are needed (such as different confirmation messages, etc.)
+// the functions onApplicationLevelError and showConfirmationDialog.
+class LLNewAgentInventoryVariablePriceResponder :
+	public LLHTTPClient::Responder
+{
+public:
+	LLNewAgentInventoryVariablePriceResponder(
+		const LLUUID& vfile_id,
+		LLAssetType::EType asset_type,
+		const LLSD& inventory_info);
+
+	LLNewAgentInventoryVariablePriceResponder(
+		const std::string& file_name,
+		LLAssetType::EType asset_type,
+		const LLSD& inventory_info);
+	virtual ~LLNewAgentInventoryVariablePriceResponder();
+
+	void errorWithContent(
+		U32 statusNum,
+		const std::string& reason,
+		const LLSD& content);
+	void result(const LLSD& content);
+
+	virtual void onApplicationLevelError(
+		const LLSD& error);
+	virtual void showConfirmationDialog(
+		S32 upload_price,
+		S32 resource_cost,
+		const std::string& confirmation_url);
+
+private:
+	class Impl;
+	Impl* mImpl;
 };
 
 struct LLBakedUploadData;
diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp
index a54c77b4a0d0cfa7253719fabbb7d78ac07e3b67..357a6582d162b582eef2561ca7fdc471c45d0bc0 100644
--- a/indra/newview/llcallbacklist.cpp
+++ b/indra/newview/llcallbacklist.cpp
@@ -115,6 +115,71 @@ void LLCallbackList::callFunctions()
 	}
 }
 
+// Shim class to allow arbitrary boost::bind
+// expressions to be run as one-time idle callbacks.
+class OnIdleCallbackOneTime
+{
+public:
+	OnIdleCallbackOneTime(nullary_func_t callable):
+		mCallable(callable)
+	{
+	}
+	static void onIdle(void *data)
+	{
+		gIdleCallbacks.deleteFunction(onIdle, data);
+		OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data);
+		self->call();
+		delete self;
+	}
+	void call()
+	{
+		mCallable();
+	}
+private:
+	nullary_func_t mCallable;
+};
+
+void doOnIdleOneTime(nullary_func_t callable)
+{
+	OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable);
+	gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor);
+}
+
+// Shim class to allow generic boost functions to be run as
+// recurring idle callbacks.  Callable should return true when done,
+// false to continue getting called.
+class OnIdleCallbackRepeating
+{
+public:
+	OnIdleCallbackRepeating(bool_func_t callable):
+		mCallable(callable)
+	{
+	}
+	// Will keep getting called until the callable returns true.
+	static void onIdle(void *data)
+	{
+		OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data);
+		bool done = self->call();
+		if (done)
+		{
+			gIdleCallbacks.deleteFunction(onIdle, data);
+			delete self;
+		}
+	}
+	bool call()
+	{
+		return mCallable();
+	}
+private:
+	bool_func_t mCallable;
+};
+
+void doOnIdleRepeating(bool_func_t callable)
+{
+	OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable);
+	gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor);
+}
+
 #ifdef _DEBUG
 
 void test1(void *data)
diff --git a/indra/newview/llcallbacklist.h b/indra/newview/llcallbacklist.h
index 07ac21f5e93762ac7f3fda255e1ba42e971e4d95..97f3bfd9ee29b6f061c76d0d4a21cb5d800da616 100644
--- a/indra/newview/llcallbacklist.h
+++ b/indra/newview/llcallbacklist.h
@@ -52,6 +52,15 @@ class LLCallbackList
 	callback_list_t	mCallbackList;
 };
 
+typedef boost::function<void ()> nullary_func_t;
+typedef boost::function<bool ()> bool_func_t;
+
+// Call a given callable once in idle loop.
+void doOnIdleOneTime(nullary_func_t callable);
+
+// Repeatedly call a callable in idle loop until it returns true.
+void doOnIdleRepeating(bool_func_t callable);
+
 extern LLCallbackList gIdleCallbacks;
 
 #endif
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index 0876c3fd99632712001a80df247d6c9240374612..b6d67899f8caab23f7a69923558bbbce6d1de24c 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -39,7 +39,9 @@
 #include "llviewerwindow.h"
 #include "llappviewer.h"
 #include "llmemoryview.h"
+#include "llsceneview.h"
 #include "llviewertexture.h"
+
 //
 // Globals
 //
@@ -83,6 +85,13 @@ void LLDebugView::init()
 	addChild(mFastTimerView);
 	mFastTimerView->setRect(rect);
 
+	gSceneView = new LLSceneView(r);
+	gSceneView->setFollowsTop();
+	gSceneView->setFollowsLeft();
+	gSceneView->setVisible(FALSE);
+	addChild(gSceneView);
+	gSceneView->setRect(rect);
+	
 	r.setLeftTopAndSize(25, rect.getHeight() - 50, (S32) (gViewerWindow->getWindowRectScaled().getWidth() * 0.75f), 
 									 (S32) (gViewerWindow->getWindowRectScaled().getHeight() * 0.75f));
 	LLMemoryView::Params mp;
@@ -103,6 +112,9 @@ void LLDebugView::init()
 	addChild(gTextureView);
 	//gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE);
 
+	
+
+
 	if(gAuditTexture)
 	{
 		r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100);
@@ -133,6 +145,7 @@ LLDebugView::~LLDebugView()
 	// These have already been deleted.  Fix the globals appropriately.
 	gDebugView = NULL;
 	gTextureView = NULL;
+	gSceneView = NULL;
 	gTextureSizeView = NULL;
 	gTextureCategoryView = NULL;
 }
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index 8106fada111b5aa02aceed2f091fafefbdc28e59..bdc12ec0e3c384ca6003dde4477a62c00bf64ba1 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -35,6 +35,7 @@
 #include "llcriticaldamp.h"
 #include "llface.h"
 #include "lllightconstants.h"
+#include "llmatrix4a.h"
 #include "llsky.h"
 #include "llsurfacepatch.h"
 #include "llviewercamera.h"
@@ -85,6 +86,7 @@ void LLDrawable::incrementVisible()
 	sCurVisible++;
 	sCurPixelAngle = (F32) gViewerWindow->getWindowHeightRaw()/LLViewerCamera::getInstance()->getView();
 }
+
 void LLDrawable::init()
 {
 	// mXform
@@ -115,6 +117,11 @@ void LLDrawable::initClass()
 
 void LLDrawable::destroy()
 {
+	if (gDebugGL)
+	{
+		gPipeline.checkReferences(this);
+	}
+
 	if (isDead())
 	{
 		sNumZombieDrawables--;
@@ -133,6 +140,7 @@ void LLDrawable::destroy()
 	{
 		llinfos << "- Zombie drawables: " << sNumZombieDrawables << llendl;
 	}*/	
+
 }
 
 void LLDrawable::markDead()
@@ -170,6 +178,11 @@ LLVOVolume* LLDrawable::getVOVolume() const
 	}
 }
 
+const LLMatrix4& LLDrawable::getRenderMatrix() const
+{ 
+	return isRoot() ? getWorldMatrix() : getParent()->getWorldMatrix();
+}
+
 BOOL LLDrawable::isLight() const
 {
 	LLViewerObject* objectp = mVObjp;
@@ -183,20 +196,30 @@ BOOL LLDrawable::isLight() const
 	}
 }
 
+static LLFastTimer::DeclareTimer FTM_CLEANUP_DRAWABLE("Cleanup Drawable");
+static LLFastTimer::DeclareTimer FTM_DEREF_DRAWABLE("Deref");
+static LLFastTimer::DeclareTimer FTM_DELETE_FACES("Faces");
+
 void LLDrawable::cleanupReferences()
 {
-	LLFastTimer t(FTM_PIPELINE);
+	LLFastTimer t(FTM_CLEANUP_DRAWABLE);
 	
-	std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
-	mFaces.clear();
+	{
+		LLFastTimer t(FTM_DELETE_FACES);
+		std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
+		mFaces.clear();
+	}
 
 	gObjectList.removeDrawable(this);
 	
 	gPipeline.unlinkDrawable(this);
 	
-	// Cleanup references to other objects
-	mVObjp = NULL;
-	mParent = NULL;
+	{
+		LLFastTimer t(FTM_DEREF_DRAWABLE);
+		// Cleanup references to other objects
+		mVObjp = NULL;
+		mParent = NULL;
+	}
 }
 
 void LLDrawable::cleanupDeadDrawables()
@@ -685,13 +708,15 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update)
 		LLVOVolume* volume = getVOVolume();
 		if (volume)
 		{
-			volume->updateRelativeXform();
-			pos = volume->getRelativeXform().getTranslation();
-			if (isStatic())
+			if (getSpatialGroup())
 			{
-				pos += volume->getRegion()->getOriginAgent();
+				pos.set(getPositionGroup().getF32ptr());
 			}
-
+			else
+			{
+				pos = getPositionAgent();
+			}
+			
 			if (isState(LLDrawable::HAS_ALPHA))
 			{
 				for (S32 i = 0; i < getNumFaces(); i++)
@@ -699,21 +724,23 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update)
 					LLFace* facep = getFace(i);
 					if (force_update || facep->getPoolType() == LLDrawPool::POOL_ALPHA)
 					{
-						LLVector3 box = (facep->mExtents[1] - facep->mExtents[0]) * 0.25f;
+						LLVector4a box;
+						box.setSub(facep->mExtents[1], facep->mExtents[0]);
+						box.mul(0.25f);
 						LLVector3 v = (facep->mCenterLocal-camera.getOrigin());
 						const LLVector3& at = camera.getAtAxis();
 						for (U32 j = 0; j < 3; j++)
 						{
-							v.mV[j] -= box.mV[j] * at.mV[j];
+							v.mV[j] -= box[j] * at.mV[j];
 						}
 						facep->mDistance = v * camera.getAtAxis();
 					}
 				}
-			}
+			}	
 		}
 		else
 		{
-			pos = LLVector3(getPositionGroup());
+			pos = LLVector3(getPositionGroup().getF32ptr());
 		}
 
 		pos -= camera.getOrigin();	
@@ -739,7 +766,7 @@ void LLDrawable::updateTexture()
 
 	if (getVOVolume())
 	{
-		if (isActive())
+		/*if (isActive())
 		{
 			if (isRoot())
 			{
@@ -749,7 +776,7 @@ void LLDrawable::updateTexture()
 			{
 				getParent()->mQuietCount = 0;
 			}
-		}
+		}*/
 				
 		gPipeline.markRebuild(this, LLDrawable::REBUILD_MATERIAL, TRUE);
 	}
@@ -762,7 +789,7 @@ BOOL LLDrawable::updateGeometry(BOOL priority)
 	return res;
 }
 
-void LLDrawable::shiftPos(const LLVector3 &shift_vector)
+void LLDrawable::shiftPos(const LLVector4a &shift_vector)
 {
 	if (isDead())
 	{
@@ -794,20 +821,19 @@ void LLDrawable::shiftPos(const LLVector3 &shift_vector)
 		for (S32 i = 0; i < getNumFaces(); i++)
 		{
 			LLFace *facep = getFace(i);
-			facep->mCenterAgent += shift_vector;
-			facep->mExtents[0] += shift_vector;
-			facep->mExtents[1] += shift_vector;
+			facep->mCenterAgent += LLVector3(shift_vector.getF32ptr());
+			facep->mExtents[0].add(shift_vector);
+			facep->mExtents[1].add(shift_vector);
 			
 			if (!volume && facep->hasGeometry())
 			{
-				facep->mVertexBuffer = NULL;
-				facep->mLastVertexBuffer = NULL;
+				facep->clearVertexBuffer();
 			}
 		}
 		
-		mExtents[0] += shift_vector;
-		mExtents[1] += shift_vector;
-		mPositionGroup += LLVector3d(shift_vector);
+		mExtents[0].add(shift_vector);
+		mExtents[1].add(shift_vector);
+		mPositionGroup.add(shift_vector);
 	}
 	else if (mSpatialBridge)
 	{
@@ -815,9 +841,9 @@ void LLDrawable::shiftPos(const LLVector3 &shift_vector)
 	}
 	else if (isAvatar())
 	{
-		mExtents[0] += shift_vector;
-		mExtents[1] += shift_vector;
-		mPositionGroup += LLVector3d(shift_vector);
+		mExtents[0].add(shift_vector);
+		mExtents[1].add(shift_vector);
+		mPositionGroup.add(shift_vector);
 	}
 	
 	mVObjp->onShift(shift_vector);
@@ -829,21 +855,26 @@ const LLVector3& LLDrawable::getBounds(LLVector3& min, LLVector3& max) const
 	return mXform.getPositionW();
 }
 
-const LLVector3* LLDrawable::getSpatialExtents() const
+const LLVector4a* LLDrawable::getSpatialExtents() const
 {
 	return mExtents;
 }
 
-void LLDrawable::setSpatialExtents(LLVector3 min, LLVector3 max)
+void LLDrawable::setSpatialExtents(const LLVector3& min, const LLVector3& max)
+{ 
+	mExtents[0].load3(min.mV); 
+	mExtents[1].load3(max.mV);
+}
+
+void LLDrawable::setSpatialExtents(const LLVector4a& min, const LLVector4a& max)
 { 
-	LLVector3 size = max - min;
 	mExtents[0] = min; 
-	mExtents[1] = max; 
+	mExtents[1] = max;
 }
 
-void LLDrawable::setPositionGroup(const LLVector3d& pos)
+void LLDrawable::setPositionGroup(const LLVector4a& pos)
 {
-	mPositionGroup.setVec(pos);
+	mPositionGroup = pos;
 }
 
 void LLDrawable::updateSpatialExtents()
@@ -857,7 +888,7 @@ void LLDrawable::updateSpatialExtents()
 	
 	if (mSpatialBridge.notNull())
 	{
-		mPositionGroup.setVec(0,0,0);
+		mPositionGroup.splat(0.f);
 	}
 }
 
@@ -910,6 +941,18 @@ void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp)
 	{
 		mSpatialGroupp->setState(LLSpatialGroup::GEOM_DIRTY);
 	}*/
+
+	if (mSpatialGroupp != groupp && getVOVolume())
+	{ //NULL out vertex buffer references for volumes on spatial group change to maintain
+		//requirement that every face vertex buffer is either NULL or points to a vertex buffer
+		//contained by its drawable's spatial group
+		for (S32 i = 0; i < getNumFaces(); ++i)
+		{
+			LLFace* facep = getFace(i);
+			facep->clearVertexBuffer();
+		}
+	}
+
 	mSpatialGroupp = groupp;
 }
 
@@ -1027,7 +1070,7 @@ BOOL LLDrawable::isVisible() const
 //=======================================
 
 LLSpatialBridge::LLSpatialBridge(LLDrawable* root, BOOL render_by_group, U32 data_mask)
-: LLSpatialPartition(data_mask, render_by_group, FALSE)
+: LLSpatialPartition(data_mask, render_by_group, GL_STREAM_DRAW_ARB)
 {
 	mDrawable = root;
 	root->setSpatialBridge(this);
@@ -1068,59 +1111,72 @@ void LLSpatialBridge::updateSpatialExtents()
 		root->rebound();
 	}
 	
-	LLXformMatrix* mat = mDrawable->getXform();
-	
-	LLVector3 offset = root->mBounds[0];
-	LLVector3 size = root->mBounds[1];
+	LLVector4a offset;
+	LLVector4a size = root->mBounds[1];
 		
-	LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
-	LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
+	//VECTORIZE THIS
+	LLMatrix4a mat;
+	mat.loadu(mDrawable->getXform()->getWorldMatrix());
+
+	LLVector4a t;
+	t.splat(0.f);
+
+	LLVector4a center;
+	mat.affineTransform(t, center);
 	
-	offset *= rotation;
-	center += offset;
+	mat.rotate(root->mBounds[0], offset);
+	center.add(offset);
 	
-	LLVector3 v[4];
+	LLVector4a v[4];
+
 	//get 4 corners of bounding box
-	v[0] = (size * rotation);
-	v[1] = (LLVector3(-size.mV[0], -size.mV[1], size.mV[2]) * rotation);
-	v[2] = (LLVector3(size.mV[0], -size.mV[1], -size.mV[2]) * rotation);
-	v[3] = (LLVector3(-size.mV[0], size.mV[1], -size.mV[2]) * rotation);
+	mat.rotate(size,v[0]);
 
-	LLVector3& newMin = mExtents[0];
-	LLVector3& newMax = mExtents[1];
+	LLVector4a scale;
+	
+	scale.set(-1.f, -1.f, 1.f);
+	scale.mul(size);
+	mat.rotate(scale, v[1]);
+	
+	scale.set(1.f, -1.f, -1.f);
+	scale.mul(size);
+	mat.rotate(scale, v[2]);
+	
+	scale.set(-1.f, 1.f, -1.f);
+	scale.mul(size);
+	mat.rotate(scale, v[3]);
+
+	
+	LLVector4a& newMin = mExtents[0];
+	LLVector4a& newMax = mExtents[1];
 	
 	newMin = newMax = center;
 	
 	for (U32 i = 0; i < 4; i++)
 	{
-		for (U32 j = 0; j < 3; j++)
-		{
-			F32 delta = fabsf(v[i].mV[j]);
-			F32 min = center.mV[j] - delta;
-			F32 max = center.mV[j] + delta;
-			
-			if (min < newMin.mV[j])
-			{
-				newMin.mV[j] = min;
-			}
-			
-			if (max > newMax.mV[j])
-			{
-				newMax.mV[j] = max;
-			}
-		}
-	}
+		LLVector4a delta;
+		delta.setAbs(v[i]);
+		LLVector4a min;
+		min.setSub(center, delta);
+		LLVector4a max;
+		max.setAdd(center, delta);
 
-	LLVector3 diagonal = newMax - newMin;
-	mRadius = diagonal.magVec() * 0.5f;
+		newMin.setMin(newMin, min);
+		newMax.setMax(newMax, max);
+	}
+	
+	LLVector4a diagonal;
+	diagonal.setSub(newMax, newMin);
+	mRadius = diagonal.getLength3().getF32() * 0.5f;
 	
-	mPositionGroup.setVec((newMin + newMax) * 0.5f);
+	mPositionGroup.setAdd(newMin,newMax);
+	mPositionGroup.mul(0.5f);
 	updateBinRadius();
 }
 
 void LLSpatialBridge::updateBinRadius()
 {
-	mBinRadius = llmin((F32) mOctree->getSize().mdV[0]*0.5f, 256.f);
+	mBinRadius = llmin( mOctree->getSize()[0]*0.5f, 256.f);
 }
 
 LLCamera LLSpatialBridge::transformCamera(LLCamera& camera)
@@ -1261,8 +1317,12 @@ void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>*
 	LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
 	group->rebound();
 	
-	LLVector3 center = (mExtents[0] + mExtents[1]) * 0.5f;
-	LLVector3 size = (mExtents[1]-mExtents[0]) * 0.5f;
+	LLVector4a center;
+	center.setAdd(mExtents[0], mExtents[1]);
+	center.mul(0.5f);
+	LLVector4a size;
+	size.setSub(mExtents[1], mExtents[0]);
+	size.mul(0.5f);
 
 	if ((LLPipeline::sShadowRender && camera_in.AABBInFrustum(center, size)) ||
 		LLPipeline::sImpostorRender ||
@@ -1375,11 +1435,11 @@ BOOL LLSpatialBridge::updateMove()
 	return TRUE;
 }
 
-void LLSpatialBridge::shiftPos(const LLVector3& vec)
+void LLSpatialBridge::shiftPos(const LLVector4a& vec)
 {
-	mExtents[0] += vec;
-	mExtents[1] += vec;
-	mPositionGroup += LLVector3d(vec);
+	mExtents[0].add(vec);
+	mExtents[1].add(vec);
+	mPositionGroup.add(vec);
 }
 
 void LLSpatialBridge::cleanupReferences()
@@ -1497,7 +1557,7 @@ F32 LLHUDBridge::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
 }
 
 
-void LLHUDBridge::shiftPos(const LLVector3& vec)
+void LLHUDBridge::shiftPos(const LLVector4a& vec)
 {
 	//don't shift hud bridges on region crossing
 }
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
index 2cea41df0a20b1a482f4ab9c6a546eebc77deaa1..9ebe1a45b44a12654a9c1ca8afb66da7941d735f 100644
--- a/indra/newview/lldrawable.h
+++ b/indra/newview/lldrawable.h
@@ -35,6 +35,7 @@
 #include "v4math.h"
 #include "m4math.h"
 #include "v4coloru.h"
+#include "llvector4a.h"
 #include "llquaternion.h"
 #include "xform.h"
 #include "llmemtype.h"
@@ -61,6 +62,17 @@ const U32 SILHOUETTE_HIGHLIGHT = 0;
 class LLDrawable : public LLRefCount
 {
 public:
+	LLDrawable(const LLDrawable& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLDrawable& operator=(const LLDrawable& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
 	static void initClass();
 
 	LLDrawable()				{ init(); }
@@ -84,19 +96,19 @@ class LLDrawable : public LLRefCount
 	LLVOVolume*	getVOVolume() const; // cast mVObjp tp LLVOVolume if OK
 
 	const LLMatrix4&      getWorldMatrix() const		{ return mXform.getWorldMatrix(); }
-	const LLMatrix4&	  getRenderMatrix() const		{ return isRoot() ? getWorldMatrix() : getParent()->getWorldMatrix(); }
+	const LLMatrix4&	  getRenderMatrix() const;
 	void				  setPosition(LLVector3 v) const { }
 	const LLVector3&	  getPosition() const			{ return mXform.getPosition(); }
 	const LLVector3&      getWorldPosition() const		{ return mXform.getPositionW(); }
 	const LLVector3		  getPositionAgent() const;
-	const LLVector3d&	  getPositionGroup() const		{ return mPositionGroup; }
+	const LLVector4a&	  getPositionGroup() const		{ return mPositionGroup; }
 	const LLVector3&	  getScale() const				{ return mCurrentScale; }
 	void				  setScale(const LLVector3& scale) { mCurrentScale = scale; }
 	const LLQuaternion&   getWorldRotation() const		{ return mXform.getWorldRotation(); }
 	const LLQuaternion&   getRotation() const			{ return mXform.getRotation(); }
 	F32			          getIntensity() const			{ return llmin(mXform.getScale().mV[0], 4.f); }
 	S32					  getLOD() const				{ return mVObjp ? mVObjp->getLOD() : 1; }
-	F64					  getBinRadius() const			{ return mBinRadius; }
+	F32					  getBinRadius() const			{ return mBinRadius; }
 	void  getMinMax(LLVector3& min,LLVector3& max) const { mXform.getMinMax(min,max); }
 	LLXformMatrix*		getXform() { return &mXform; }
 
@@ -150,7 +162,7 @@ class LLDrawable : public LLRefCount
 		
 	void updateSpecialHoverCursor(BOOL enabled);
 
-	virtual void shiftPos(const LLVector3 &shift_vector);
+	virtual void shiftPos(const LLVector4a &shift_vector);
 
 	S32 getGeneration() const					{ return mGeneration; }
 
@@ -168,11 +180,12 @@ class LLDrawable : public LLRefCount
 	const LLVector3& getBounds(LLVector3& min, LLVector3& max) const;
 	virtual void updateSpatialExtents();
 	virtual void updateBinRadius();
-	const LLVector3* getSpatialExtents() const;
-	void setSpatialExtents(LLVector3 min, LLVector3 max);
-	void setPositionGroup(const LLVector3d& pos);
-	void setPositionGroup(const LLVector3& pos) { setPositionGroup(LLVector3d(pos)); }
+	const LLVector4a* getSpatialExtents() const;
+	void setSpatialExtents(const LLVector3& min, const LLVector3& max);
+	void setSpatialExtents(const LLVector4a& min, const LLVector4a& max);
 
+	void setPositionGroup(const LLVector4a& pos);
+	
 	void setRenderType(S32 type) 				{ mRenderType = type; }
 	BOOL isRenderType(S32 type) 				{ return mRenderType == type; }
 	S32  getRenderType()						{ return mRenderType; }
@@ -232,37 +245,44 @@ class LLDrawable : public LLRefCount
 	
 	typedef enum e_drawable_flags
 	{
- 		IN_REBUILD_Q1	= 0x00000002,
- 		IN_REBUILD_Q2	= 0x00000004,
- 		IN_LIGHT_Q		= 0x00000008,
-		EARLY_MOVE		= 0x00000010,
-		MOVE_UNDAMPED	= 0x00000020,
-		ON_MOVE_LIST	= 0x00000040,
-		USE_BACKLIGHT	= 0x00000080,
-		UV				= 0x00000100,
-		UNLIT			= 0x00000200,
-		LIGHT			= 0x00000400,
-		LIGHTING_BUILT	= 0x00000800,
-		REBUILD_VOLUME  = 0x00001000,	//volume changed LOD or parameters, or vertex buffer changed
-		REBUILD_TCOORD	= 0x00002000,	//texture coordinates changed
-		REBUILD_COLOR	= 0x00004000,	//color changed
-		REBUILD_POSITION= 0x00010000,	//vertex positions/normals changed
+ 		IN_REBUILD_Q1	= 0x00000001,
+ 		IN_REBUILD_Q2	= 0x00000002,
+ 		IN_LIGHT_Q		= 0x00000004,
+		EARLY_MOVE		= 0x00000008,
+		MOVE_UNDAMPED	= 0x00000010,
+		ON_MOVE_LIST	= 0x00000020,
+		USE_BACKLIGHT	= 0x00000040,
+		UV				= 0x00000080,
+		UNLIT			= 0x00000100,
+		LIGHT			= 0x00000200,
+		LIGHTING_BUILT	= 0x00000400,
+		REBUILD_VOLUME  = 0x00000800,	//volume changed LOD or parameters, or vertex buffer changed
+		REBUILD_TCOORD	= 0x00001000,	//texture coordinates changed
+		REBUILD_COLOR	= 0x00002000,	//color changed
+		REBUILD_POSITION= 0x00004000,	//vertex positions/normals changed
 		REBUILD_GEOMETRY= REBUILD_POSITION|REBUILD_TCOORD|REBUILD_COLOR,
 		REBUILD_MATERIAL= REBUILD_TCOORD|REBUILD_COLOR,
 		REBUILD_ALL		= REBUILD_GEOMETRY|REBUILD_VOLUME,
-		ON_SHIFT_LIST	= 0x00100000,
-		BLOCKER			= 0x00400000,
-		ACTIVE			= 0x00800000,
-		DEAD			= 0x01000000,
-		INVISIBLE		= 0x02000000, // stay invisible until flag is cleared
- 		NEARBY_LIGHT	= 0x04000000, // In gPipeline.mNearbyLightSet
-		BUILT			= 0x08000000,
-		FORCE_INVISIBLE = 0x10000000, // stay invis until CLEAR_INVISIBLE is set (set of orphaned)
-		CLEAR_INVISIBLE = 0x20000000, // clear FORCE_INVISIBLE next draw frame
-		REBUILD_SHADOW =  0x40000000,
-		HAS_ALPHA		= 0x80000000,
+		REBUILD_RIGGED	= 0x00008000,
+		ON_SHIFT_LIST	= 0x00010000,
+		BLOCKER			= 0x00020000,
+		ACTIVE			= 0x00040000,
+		DEAD			= 0x00080000,
+		INVISIBLE		= 0x00100000, // stay invisible until flag is cleared
+ 		NEARBY_LIGHT	= 0x00200000, // In gPipeline.mNearbyLightSet
+		BUILT			= 0x00400000,
+		FORCE_INVISIBLE = 0x00800000, // stay invis until CLEAR_INVISIBLE is set (set of orphaned)
+		CLEAR_INVISIBLE = 0x01000000, // clear FORCE_INVISIBLE next draw frame
+		REBUILD_SHADOW =  0x02000000,
+		HAS_ALPHA		= 0x04000000,
+		RIGGED			= 0x08000000,
 	} EDrawableFlags;
 
+private: //aligned members
+	LLVector4a		mExtents[2];
+	LLVector4a		mPositionGroup;
+	
+public:
 	LLXformMatrix       mXform;
 
 	// vis data
@@ -292,9 +312,7 @@ class LLDrawable : public LLRefCount
 	
 	mutable U32		mVisible;
 	F32				mRadius;
-	LLVector3		mExtents[2];
-	LLVector3d		mPositionGroup;
-	F64				mBinRadius;
+	F32				mBinRadius;
 	S32				mGeneration;
 	
 	LLVector3		mCurrentScale;
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index ba576ff97f260365af30c2b8c413c9d8f0d81319..25e4bc847c253dcfd41423ef3575751c21c073c5 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -243,11 +243,6 @@ void LLFacePool::dirtyTextures(const std::set<LLViewerFetchedTexture*>& textures
 {
 }
 
-BOOL LLFacePool::moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data)
-{
-	return TRUE;
-}
-
 // static
 S32 LLFacePool::drawLoop(face_array_t& face_list)
 {
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
index 1d6f99d346eacc551f8e4486ee46f6e1a9d5e89c..d3fd9ead0d4ef42a88f3a0d0230ffc03d8586b2b 100644
--- a/indra/newview/lldrawpool.h
+++ b/indra/newview/lldrawpool.h
@@ -47,14 +47,14 @@ class LLDrawPool
 	{
 		// Correspond to LLPipeline render type
 		POOL_SIMPLE = 1,
+		POOL_GROUND,
+		POOL_FULLBRIGHT,
+		POOL_BUMP,
 		POOL_TERRAIN,	
-		POOL_TREE,
 		POOL_SKY,
 		POOL_WL_SKY,
-		POOL_GROUND,
+		POOL_TREE,
 		POOL_GRASS,
-		POOL_FULLBRIGHT,
-		POOL_BUMP,
 		POOL_INVISIBLE, // see below *
 		POOL_AVATAR,
 		POOL_VOIDWATER,
@@ -182,8 +182,6 @@ class LLFacePool : public LLDrawPool
 	virtual void resetDrawOrders();
 	void resetAll();
 
-	BOOL moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data = FALSE);
-
 	void destroy();
 
 	void buildEdges();
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index a2428d2de0aa09c35f0886119bd72dbe88905518..8b5a2ce78182d6e4147c73cb09430aaae7453f88 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -103,16 +103,40 @@ void LLDrawPoolAlpha::renderDeferred(S32 pass)
 
 S32 LLDrawPoolAlpha::getNumPostDeferredPasses() 
 { 
-	return 1; 
+	if (LLPipeline::sImpostorRender)
+	{ //skip depth buffer filling pass when rendering impostors
+		return 1;
+	}
+	else if (gSavedSettings.getBOOL("RenderDepthOfField"))
+	{
+		return 2; 
+	}
+	else
+	{
+		return 1;
+	}
 }
 
 void LLDrawPoolAlpha::beginPostDeferredPass(S32 pass) 
 { 
 	LLFastTimer t(FTM_RENDER_ALPHA);
 
-	simple_shader = &gDeferredAlphaProgram;
-	fullbright_shader = &gDeferredFullbrightProgram;
-	
+	if (pass == 0)
+	{
+		simple_shader = &gDeferredAlphaProgram;
+		fullbright_shader = &gDeferredFullbrightProgram;
+	}
+	else
+	{
+		//update depth buffer sampler
+		gPipeline.mScreen.flush();
+		gPipeline.mDeferredDepth.copyContents(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), gPipeline.mDeferredScreen.getHeight(),
+							0, 0, gPipeline.mDeferredDepth.getWidth(), gPipeline.mDeferredDepth.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);	
+		gPipeline.mDeferredDepth.bindTarget();
+		simple_shader = NULL;
+		fullbright_shader = NULL;
+	}
+
 	deferred_render = TRUE;
 	if (mVertexShaderLevel > 0)
 	{
@@ -124,6 +148,13 @@ void LLDrawPoolAlpha::beginPostDeferredPass(S32 pass)
 
 void LLDrawPoolAlpha::endPostDeferredPass(S32 pass) 
 { 
+
+	if (pass == 1)
+	{
+		gPipeline.mDeferredDepth.flush();
+		gPipeline.mScreen.bindTarget();
+	}
+
 	deferred_render = FALSE;
 	endRenderPass(pass);
 }
@@ -174,9 +205,16 @@ void LLDrawPoolAlpha::render(S32 pass)
 
 	LLGLSPipelineAlpha gls_pipeline_alpha;
 
-	gGL.setColorMask(true, true);
+	if (deferred_render && pass == 1)
+	{ //depth only
+		gGL.setColorMask(false, false);
+	}
+	else
+	{
+		gGL.setColorMask(true, true);
+	}
 
-	if (LLPipeline::sAutoMaskAlphaNonDeferred && !deferred_render)
+	if (LLPipeline::sAutoMaskAlphaNonDeferred)
 	{
 		mColorSFactor = LLRender::BF_ONE;  // }
 		mColorDFactor = LLRender::BF_ZERO; // } these are like disabling blend on the color channels, but we're still blending on the alpha channel so that we can suppress glow
@@ -192,7 +230,10 @@ void LLDrawPoolAlpha::render(S32 pass)
 				simple_shader->bind();
 				pushBatches(LLRenderPass::PASS_ALPHA_MASK, getVertexDataMask());
 			}
-			fullbright_shader->bind();
+			if (fullbright_shader)
+			{
+				fullbright_shader->bind();
+			}
 			pushBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, getVertexDataMask());
 			LLGLSLShader::bindNoShader();
 		}
@@ -206,18 +247,42 @@ void LLDrawPoolAlpha::render(S32 pass)
 		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 	}
 
-	LLGLDepthTest depth(GL_TRUE, LLDrawPoolWater::sSkipScreenCopy ? GL_TRUE : GL_FALSE);
+	LLGLDepthTest depth(GL_TRUE, LLDrawPoolWater::sSkipScreenCopy || 
+				(deferred_render && pass == 1) ? GL_TRUE : GL_FALSE);
 
-	mColorSFactor = LLRender::BF_SOURCE_ALPHA;           // } regular alpha blend
-	mColorDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // }
-	mAlphaSFactor = LLRender::BF_ZERO;                         // } glow suppression
-	mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;       // }
-	gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
+	if (deferred_render && pass == 1)
+	{
+		gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.33f);
+		gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
+	}
+	else
+	{
+		mColorSFactor = LLRender::BF_SOURCE_ALPHA;           // } regular alpha blend
+		mColorDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // }
+		mAlphaSFactor = LLRender::BF_ZERO;                         // } glow suppression
+		mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;       // }
+		gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
+
+		if (LLPipeline::sImpostorRender)
+		{
+			gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f);
+		}
+		else
+		{
+			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
+		}
+	}
 
 	renderAlpha(getVertexDataMask());
 
 	gGL.setColorMask(true, false);
 
+	if (deferred_render && pass == 1)
+	{
+		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
+		gGL.setSceneBlendType(LLRender::BT_ALPHA);
+	}
+
 	if (deferred_render && current_shader != NULL)
 	{
 		gPipeline.unbindDeferredShader(*current_shader);
@@ -276,22 +341,10 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask)
 	BOOL light_enabled = TRUE;
 	S32 diffuse_channel = 0;
 
-	//BOOL is_particle = FALSE;
 	BOOL use_shaders = (LLPipeline::sUnderWaterRender && gPipeline.canUseVertexShaders())
 		|| gPipeline.canUseWindLightShadersOnObjects();
 	
-	// check to see if it's a particle and if it's "close"
-	{
-		if (LLPipeline::sImpostorRender)
-		{
-			gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f);
-		}
-		else
-		{
-			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
-		}
-	}
-
+	
 	for (LLCullResult::sg_list_t::iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i)
 	{
 		LLSpatialGroup* group = *i;
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index dbd5da31a63d995cd24cdd3a2e94335dd7226edd..645c7ebcaeb933d586074da457beb80c55518d3d 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -31,15 +31,21 @@
 
 #include "llvoavatar.h"
 #include "m3math.h"
+#include "llmatrix4a.h"
 
+#include "llagent.h" //for gAgent.needsRenderAvatar()
 #include "lldrawable.h"
+#include "lldrawpoolbump.h"
 #include "llface.h"
+#include "llmeshrepository.h"
 #include "llsky.h"
 #include "llviewercamera.h"
 #include "llviewerregion.h"
 #include "noise.h"
 #include "pipeline.h"
 #include "llviewershadermgr.h"
+#include "llvovolume.h"
+#include "llvolume.h"
 #include "llappviewer.h"
 #include "llrendersphere.h"
 #include "llviewerpartsim.h"
@@ -47,10 +53,15 @@
 static U32 sDataMask = LLDrawPoolAvatar::VERTEX_DATA_MASK;
 static U32 sBufferUsage = GL_STREAM_DRAW_ARB;
 static U32 sShaderLevel = 0;
-static LLGLSLShader* sVertexProgram = NULL;
 
+
+LLGLSLShader* LLDrawPoolAvatar::sVertexProgram = NULL;
 BOOL	LLDrawPoolAvatar::sSkipOpaque = FALSE;
 BOOL	LLDrawPoolAvatar::sSkipTransparent = FALSE;
+S32 LLDrawPoolAvatar::sDiffuseChannel = 0;
+
+
+static bool is_deferred_render = false;
 
 extern BOOL gUseGLPick;
 
@@ -86,7 +97,7 @@ BOOL gAvatarEmbossBumpMap = FALSE;
 static BOOL sRenderingSkinned = FALSE;
 S32 normal_channel = -1;
 S32 specular_channel = -1;
-S32 diffuse_channel = -1;
+S32 cube_channel = -1;
 
 static LLFastTimer::DeclareTimer FTM_SHADOW_AVATAR("Avatar Shadow");
 
@@ -142,21 +153,17 @@ LLMatrix4& LLDrawPoolAvatar::getModelView()
 //-----------------------------------------------------------------------------
 
 
-S32 LLDrawPoolAvatar::getNumDeferredPasses()
-{
-	return getNumPasses();
-}
 
 void LLDrawPoolAvatar::beginDeferredPass(S32 pass)
 {
 	LLFastTimer t(FTM_RENDER_CHARACTERS);
 	
 	sSkipTransparent = TRUE;
-
+	is_deferred_render = true;
+	
 	if (LLPipeline::sImpostorRender)
-	{
-		beginDeferredSkinned();
-		return;
+	{ //impostor pass does not have rigid or impostor rendering
+		pass += 2;
 	}
 
 	switch (pass)
@@ -170,6 +177,12 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass)
 	case 2:
 		beginDeferredSkinned();
 		break;
+	case 3:
+		beginDeferredRiggedSimple();
+		break;
+	case 4:
+		beginDeferredRiggedBump();
+		break;
 	}
 }
 
@@ -178,11 +191,11 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass)
 	LLFastTimer t(FTM_RENDER_CHARACTERS);
 
 	sSkipTransparent = FALSE;
+	is_deferred_render = false;
 
 	if (LLPipeline::sImpostorRender)
 	{
-		endDeferredSkinned();
-		return;
+		pass += 2;
 	}
 
 	switch (pass)
@@ -196,6 +209,12 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass)
 	case 2:
 		endDeferredSkinned();
 		break;
+	case 3:
+		endDeferredRiggedSimple();
+		break;
+	case 4:
+		endDeferredRiggedBump();
+		break;
 	}
 }
 
@@ -206,10 +225,35 @@ void LLDrawPoolAvatar::renderDeferred(S32 pass)
 
 S32 LLDrawPoolAvatar::getNumPostDeferredPasses()
 {
-	return 1;
+	return 6;
 }
 
 void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass)
+{
+	switch (pass)
+	{
+	case 0:
+		beginPostDeferredAlpha();
+		break;
+	case 1:
+		beginRiggedFullbright();
+		break;
+	case 2:
+		beginRiggedFullbrightShiny();
+		break;
+	case 3:
+		beginDeferredRiggedAlpha();
+		break;
+	case 4:
+		beginRiggedFullbrightAlpha();
+		break;
+	case 5:
+		beginRiggedGlow();
+		break;
+	}
+}
+
+void LLDrawPoolAvatar::beginPostDeferredAlpha()
 {
 	sSkipOpaque = TRUE;
 	sShaderLevel = mVertexShaderLevel;
@@ -219,10 +263,54 @@ void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass)
 
 	gPipeline.bindDeferredShader(*sVertexProgram);
 	
+	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 	enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
 }
 
+void LLDrawPoolAvatar::beginDeferredRiggedAlpha()
+{
+	sVertexProgram = &gDeferredSkinnedAlphaProgram;
+	gPipeline.bindDeferredShader(*sVertexProgram);
+	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+	gPipeline.enableLightsDynamic();
+}
+
+void LLDrawPoolAvatar::endDeferredRiggedAlpha()
+{
+	LLVertexBuffer::unbind();
+	gPipeline.unbindDeferredShader(*sVertexProgram);
+	sDiffuseChannel = 0;
+	LLVertexBuffer::sWeight4Loc = -1;
+	sVertexProgram = NULL;
+}
+
 void LLDrawPoolAvatar::endPostDeferredPass(S32 pass)
+{
+	switch (pass)
+	{
+	case 0:
+		endPostDeferredAlpha();
+		break;
+	case 1:
+		endRiggedFullbright();
+		break;
+	case 2:
+		endRiggedFullbrightShiny();
+		break;
+	case 3:
+		endDeferredRiggedAlpha();
+		break;
+	case 4:
+		endRiggedFullbrightAlpha();
+		break;
+	case 5:
+		endRiggedGlow();
+		break;
+	}
+}
+
+void LLDrawPoolAvatar::endPostDeferredAlpha()
 {
 	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
 	sRenderingSkinned = FALSE;
@@ -230,53 +318,88 @@ void LLDrawPoolAvatar::endPostDeferredPass(S32 pass)
 	disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
 	
 	gPipeline.unbindDeferredShader(*sVertexProgram);
-
+	sDiffuseChannel = 0;
 	sShaderLevel = mVertexShaderLevel;
 }
 
 void LLDrawPoolAvatar::renderPostDeferred(S32 pass)
 {
-	render(2); //pass 2 = skinned
+	const S32 actual_pass[] =
+	{ //map post deferred pass numbers to what render() expects
+		2, //skinned
+		4, // rigged fullbright
+		6, //rigged fullbright shiny
+		7, //rigged alpha
+		8, //rigged fullbright alpha
+		9, //rigged glow
+	};
+
+	pass = actual_pass[pass];
+
+	if (LLPipeline::sImpostorRender)
+	{ //HACK for impostors so actual pass ends up being proper pass
+		pass -= 2;
+	}
+
+	render(pass);
 }
 
 
 S32 LLDrawPoolAvatar::getNumShadowPasses()
 {
-	return 1;
+	return 2;
 }
 
 void LLDrawPoolAvatar::beginShadowPass(S32 pass)
 {
 	LLFastTimer t(FTM_SHADOW_AVATAR);
-	sVertexProgram = &gDeferredAvatarShadowProgram;
-	if (sShaderLevel > 0)
+
+	if (pass == 0)
 	{
-		gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX];
-	}
-	gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.2f);
-	
-	glColor4f(1,1,1,1);
+		sVertexProgram = &gDeferredAvatarShadowProgram;
+		if (sShaderLevel > 0)
+		{
+			gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX];
+		}
+		gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.2f);
+		
+		glColor4f(1,1,1,1);
 
-	if ((sShaderLevel > 0))  // for hardware blending
+		if ((sShaderLevel > 0))  // for hardware blending
+		{
+			sRenderingSkinned = TRUE;
+			sVertexProgram->bind();
+			enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+		}
+	}
+	else
 	{
-		sRenderingSkinned = TRUE;
+		sVertexProgram = &gDeferredAttachmentShadowProgram;
+		sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 		sVertexProgram->bind();
-		enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+		LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
 	}
-
 }
 
 void LLDrawPoolAvatar::endShadowPass(S32 pass)
 {
 	LLFastTimer t(FTM_SHADOW_AVATAR);
-	if (sShaderLevel > 0)
+	if (pass == 0)
 	{
-		sRenderingSkinned = FALSE;
+		if (sShaderLevel > 0)
+		{
+			sRenderingSkinned = FALSE;
+			sVertexProgram->unbind();
+			disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+		}
+	}
+	else
+	{
+		LLVertexBuffer::unbind();
 		sVertexProgram->unbind();
-		disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+		LLVertexBuffer::sWeight4Loc = -1;
+		sVertexProgram = NULL;
 	}
-
-	gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 }
 
 void LLDrawPoolAvatar::renderShadow(S32 pass)
@@ -306,26 +429,66 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)
 		return;
 	}
 	
-	if (sShaderLevel > 0)
+	if (pass == 0)
 	{
-		gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX];
-	}
-
-	avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+		if (sShaderLevel > 0)
+		{
+			gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX];
+		}
 
+		avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+	}
+	else
+	{
+		renderRigged(avatarp, RIGGED_SIMPLE);
+		renderRigged(avatarp, RIGGED_ALPHA);
+		renderRigged(avatarp, RIGGED_FULLBRIGHT);
+		renderRigged(avatarp, RIGGED_FULLBRIGHT_SHINY);
+		renderRigged(avatarp, RIGGED_SHINY);
+		renderRigged(avatarp, RIGGED_FULLBRIGHT_ALPHA);
+	}
 }
 
 S32 LLDrawPoolAvatar::getNumPasses()
 {
-	return LLPipeline::sImpostorRender ? 1 : 3;
+	if (LLPipeline::sImpostorRender)
+	{
+		return 8;
+	}
+	else 
+	{
+		return 10;
+	}
+	if (LLPipeline::sImpostorRender)
+	{
+		return 1;
+	}
+	else 
+	{
+		return 3;
+	}
+}
+
+
+S32 LLDrawPoolAvatar::getNumDeferredPasses()
+{
+	if (LLPipeline::sImpostorRender)
+	{
+		return 3;
+	}
+	else
+	{
+		return 5;
+	}
 }
 
+
 void LLDrawPoolAvatar::render(S32 pass)
 {
 	LLFastTimer t(FTM_RENDER_CHARACTERS);
 	if (LLPipeline::sImpostorRender)
 	{
-		renderAvatars(NULL, 2);
+		renderAvatars(NULL, pass+2);
 		return;
 	}
 
@@ -338,10 +501,14 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass)
 	//reset vertex buffer mappings
 	LLVertexBuffer::unbind();
 
+	if (pass == 0)
+	{ //make sure no stale colors are left over from a previous render
+		glColor4f(1,1,1,1);
+	}
+
 	if (LLPipeline::sImpostorRender)
-	{
-		beginSkinned();
-		return;
+	{ //impostor render does not have impostors or rigid rendering
+		pass += 2;
 	}
 
 	switch (pass)
@@ -355,6 +522,27 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass)
 	case 2:
 		beginSkinned();
 		break;
+	case 3:
+		beginRiggedSimple();
+		break;
+	case 4:
+		beginRiggedFullbright();
+		break;
+	case 5:
+		beginRiggedShinySimple();
+		break;
+	case 6:
+		beginRiggedFullbrightShiny();
+		break;
+	case 7:
+		beginRiggedAlpha();
+		break;
+	case 8:
+		beginRiggedFullbrightAlpha();
+		break;
+	case 9:
+		beginRiggedGlow();
+		break;
 	}
 }
 
@@ -364,8 +552,7 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass)
 
 	if (LLPipeline::sImpostorRender)
 	{
-		endSkinned();
-		return;
+		pass += 2;		
 	}
 
 	switch (pass)
@@ -378,6 +565,28 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass)
 		break;
 	case 2:
 		endSkinned();
+		break;
+	case 3:
+		endRiggedSimple();
+		break;
+	case 4:
+		endRiggedFullbright();
+		break;
+	case 5:
+		endRiggedShinySimple();
+		break;
+	case 6:
+		endRiggedFullbrightShiny();
+		break;
+	case 7:
+		endRiggedAlpha();
+		break;
+	case 8:
+		endRiggedFullbrightAlpha();
+		break;
+	case 9:
+		endRiggedGlow();
+		break;
 	}
 }
 
@@ -390,7 +599,7 @@ void LLDrawPoolAvatar::beginImpostor()
 	}
 
 	gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
-	diffuse_channel = 0;
+	sDiffuseChannel = 0;
 }
 
 void LLDrawPoolAvatar::endImpostor()
@@ -441,9 +650,9 @@ void LLDrawPoolAvatar::beginDeferredImpostor()
 
 	sVertexProgram = &gDeferredImpostorProgram;
 
-	normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL);
 	specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP);
-	diffuse_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL);
+	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 
 	sVertexProgram->bind();
 }
@@ -563,83 +772,334 @@ void LLDrawPoolAvatar::endSkinned()
 	gGL.getTexUnit(0)->activate();
 }
 
-void LLDrawPoolAvatar::beginDeferredSkinned()
+void LLDrawPoolAvatar::beginRiggedSimple()
 {
-	sShaderLevel = mVertexShaderLevel;
-	sVertexProgram = &gDeferredAvatarProgram;
-
-	sRenderingSkinned = TRUE;
-
-	sVertexProgram->bind();
-	
-	enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+	if (sShaderLevel > 0)
+	{
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gSkinnedObjectSimpleWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gSkinnedObjectSimpleProgram;
+		}
+	}
+	else
+	{
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gObjectSimpleWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gObjectSimpleProgram;
+		}
+	}
 
-	gGL.getTexUnit(0)->activate();
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
+	{
+		sDiffuseChannel = 0;
+		sVertexProgram->bind();
+		LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+	}
 }
 
-void LLDrawPoolAvatar::endDeferredSkinned()
+void LLDrawPoolAvatar::endRiggedSimple()
 {
-	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
-	sRenderingSkinned = FALSE;
-	disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
-	sVertexProgram->unbind();
+	LLVertexBuffer::unbind();
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
+	{
+		sVertexProgram->unbind();
+		sVertexProgram = NULL;
+		LLVertexBuffer::sWeight4Loc = -1;
+	}
+}
 
-	sShaderLevel = mVertexShaderLevel;
+void LLDrawPoolAvatar::beginRiggedAlpha()
+{
+	beginRiggedSimple();
+}
 
-	gGL.getTexUnit(0)->activate();
+void LLDrawPoolAvatar::endRiggedAlpha()
+{
+	endRiggedSimple();
 }
 
 
-void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
+void LLDrawPoolAvatar::beginRiggedFullbrightAlpha()
 {
-	if (pass == -1)
-	{
-		for (S32 i = 1; i < getNumPasses(); i++)
-		{ //skip foot shadows
-			prerender();
-			beginRenderPass(i);
-			renderAvatars(single_avatar, i);
-			endRenderPass(i);
-		}
+	beginRiggedFullbright();
+}
 
-		return;
-	}
+void LLDrawPoolAvatar::endRiggedFullbrightAlpha()
+{
+	endRiggedFullbright();
+}
 
-	if (mDrawFace.empty() && !single_avatar)
-	{
-		return;
-	}
+void LLDrawPoolAvatar::beginRiggedGlow()
+{
+	beginRiggedFullbright();
+}
 
-	LLVOAvatar *avatarp;
+void LLDrawPoolAvatar::endRiggedGlow()
+{
+	endRiggedFullbright();
+}
 
-	if (single_avatar)
+void LLDrawPoolAvatar::beginRiggedFullbright()
+{
+	if (sShaderLevel > 0)
 	{
-		avatarp = single_avatar;
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gSkinnedObjectFullbrightWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gSkinnedObjectFullbrightProgram;
+		}
 	}
 	else
 	{
-		const LLFace *facep = mDrawFace[0];
-		if (!facep->getDrawable())
+		if (LLPipeline::sUnderWaterRender)
 		{
-			return;
+			sVertexProgram = &gObjectFullbrightWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gObjectFullbrightProgram;
 		}
-		avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
 	}
 
-    if (avatarp->isDead() || avatarp->mDrawable.isNull())
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
 	{
-		return;
+		sDiffuseChannel = 0;
+		sVertexProgram->bind();
+		LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
 	}
+}
 
-	if (!single_avatar && !avatarp->isFullyLoaded() )
+void LLDrawPoolAvatar::endRiggedFullbright()
+{
+	LLVertexBuffer::unbind();
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
 	{
-		if (pass==0 && (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) || LLViewerPartSim::getMaxPartCount() <= 0))
-		{
-			// debug code to draw a sphere in place of avatar
-			gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
-			gGL.setColorMask(true, true);
-			LLVector3 pos = avatarp->getPositionAgent();
-			gGL.color4f(1.0f, 1.0f, 1.0f, 0.7f);
+		sVertexProgram->unbind();
+		sVertexProgram = NULL;
+		LLVertexBuffer::sWeight4Loc = -1;
+	}
+}
+
+void LLDrawPoolAvatar::beginRiggedShinySimple()
+{
+	if (sShaderLevel > 0)
+	{
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gSkinnedObjectShinySimpleWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gSkinnedObjectShinySimpleProgram;
+		}
+	}
+	else
+	{
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gObjectShinyWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gObjectShinyProgram;
+		}
+	}
+
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
+	{
+		sVertexProgram->bind();
+		LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
+		LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+	}
+}
+
+void LLDrawPoolAvatar::endRiggedShinySimple()
+{
+	LLVertexBuffer::unbind();
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
+	{
+		LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
+		sVertexProgram->unbind();
+		sVertexProgram = NULL;
+		LLVertexBuffer::sWeight4Loc = -1;
+	}
+}
+
+void LLDrawPoolAvatar::beginRiggedFullbrightShiny()
+{
+	if (sShaderLevel > 0)
+	{
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gSkinnedObjectFullbrightShinyWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gSkinnedObjectFullbrightShinyProgram;
+		}
+	}
+	else
+	{
+		if (LLPipeline::sUnderWaterRender)
+		{
+			sVertexProgram = &gObjectFullbrightShinyWaterProgram;
+		}
+		else
+		{
+			sVertexProgram = &gObjectFullbrightShinyProgram;
+		}
+	}
+
+
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
+	{
+		sVertexProgram->bind();
+		LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
+		LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+	}
+}
+
+void LLDrawPoolAvatar::endRiggedFullbrightShiny()
+{
+	LLVertexBuffer::unbind();
+	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
+	{
+		LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
+		sVertexProgram->unbind();
+		sVertexProgram = NULL;
+		LLVertexBuffer::sWeight4Loc = -1;
+	}
+}
+
+
+void LLDrawPoolAvatar::beginDeferredRiggedSimple()
+{
+	sVertexProgram = &gDeferredSkinnedDiffuseProgram;
+	sDiffuseChannel = 0;
+	sVertexProgram->bind();
+	LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+}
+
+void LLDrawPoolAvatar::endDeferredRiggedSimple()
+{
+	LLVertexBuffer::unbind();
+	sVertexProgram->unbind();
+	LLVertexBuffer::sWeight4Loc = -1;
+	sVertexProgram = NULL;
+}
+
+void LLDrawPoolAvatar::beginDeferredRiggedBump()
+{
+	sVertexProgram = &gDeferredSkinnedBumpProgram;
+	sVertexProgram->bind();
+	normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::BUMP_MAP);
+	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+}
+
+void LLDrawPoolAvatar::endDeferredRiggedBump()
+{
+	LLVertexBuffer::unbind();
+	sVertexProgram->disableTexture(LLViewerShaderMgr::BUMP_MAP);
+	sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	sVertexProgram->unbind();
+	LLVertexBuffer::sWeight4Loc = -1;
+	normal_channel = -1;
+	sDiffuseChannel = 0;
+	sVertexProgram = NULL;
+}
+
+void LLDrawPoolAvatar::beginDeferredSkinned()
+{
+	sShaderLevel = mVertexShaderLevel;
+	sVertexProgram = &gDeferredAvatarProgram;
+
+	sRenderingSkinned = TRUE;
+
+	sVertexProgram->bind();
+	
+	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+
+	gGL.getTexUnit(0)->activate();
+}
+
+void LLDrawPoolAvatar::endDeferredSkinned()
+{
+	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+	sRenderingSkinned = FALSE;
+	disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]);
+	sVertexProgram->unbind();
+
+	sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+
+	sShaderLevel = mVertexShaderLevel;
+
+	gGL.getTexUnit(0)->activate();
+}
+
+
+void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
+{
+	if (pass == -1)
+	{
+		for (S32 i = 1; i < getNumPasses(); i++)
+		{ //skip foot shadows
+			prerender();
+			beginRenderPass(i);
+			renderAvatars(single_avatar, i);
+			endRenderPass(i);
+		}
+
+		return;
+	}
+
+	if (mDrawFace.empty() && !single_avatar)
+	{
+		return;
+	}
+
+	LLVOAvatar *avatarp;
+
+	if (single_avatar)
+	{
+		avatarp = single_avatar;
+	}
+	else
+	{
+		const LLFace *facep = mDrawFace[0];
+		if (!facep->getDrawable())
+		{
+			return;
+		}
+		avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
+	}
+
+    if (avatarp->isDead() || avatarp->mDrawable.isNull())
+	{
+		return;
+	}
+
+	if (!single_avatar && !avatarp->isFullyLoaded() )
+	{
+		if (pass==0 && (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) || LLViewerPartSim::getMaxPartCount() <= 0))
+		{
+			// debug code to draw a sphere in place of avatar
+			gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
+			gGL.setColorMask(true, true);
+			LLVector3 pos = avatarp->getPositionAgent();
+			gGL.color4f(1.0f, 1.0f, 1.0f, 0.7f);
 			
 			gGL.pushMatrix();	 
 			gGL.translatef((F32)(pos.mV[VX]),	 
@@ -668,8 +1128,6 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 		return;
 	}
 
-    LLOverrideFaceColor color(this, 1.0f, 1.0f, 1.0f, 1.0f);
-
 	if (pass == 0)
 	{
 		if (!LLPipeline::sReflectionRender)
@@ -679,7 +1137,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 
 		if (impostor)
 		{
-			if (LLPipeline::sRenderDeferred && avatarp->mImpostor.isComplete()) 
+			if (LLPipeline::sRenderDeferred && !LLPipeline::sReflectionRender && avatarp->mImpostor.isComplete()) 
 			{
 				if (normal_channel > -1)
 				{
@@ -690,7 +1148,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 					avatarp->mImpostor.bindTexture(1, specular_channel);
 				}
 			}
-			avatarp->renderImpostor(LLColor4U(255,255,255,255), diffuse_channel);
+			avatarp->renderImpostor(LLColor4U(255,255,255,255), sDiffuseChannel);
 		}
 		return;
 	}
@@ -706,6 +1164,88 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 		avatarp->renderRigid();
 		return;
 	}
+
+	if (pass == 3)
+	{
+		if (is_deferred_render)
+		{
+			renderDeferredRiggedSimple(avatarp);
+		}
+		else
+		{
+			renderRiggedSimple(avatarp);
+		}
+		return;
+	}
+
+	if (pass == 4)
+	{
+		if (is_deferred_render)
+		{
+			renderDeferredRiggedBump(avatarp);
+		}
+		else
+		{
+			renderRiggedFullbright(avatarp);
+		}
+
+		return;
+	}
+
+	if (pass == 5)
+	{
+		renderRiggedShinySimple(avatarp);
+		return;
+	}
+
+	if (pass == 6)
+	{
+		renderRiggedFullbrightShiny(avatarp);
+		return;
+	}
+
+	if (pass >= 7 && pass < 9)
+	{
+		LLGLEnable blend(GL_BLEND);
+
+		gGL.setColorMask(true, true);
+		gGL.blendFunc(LLRender::BF_SOURCE_ALPHA,
+					  LLRender::BF_ONE_MINUS_SOURCE_ALPHA,
+					  LLRender::BF_ZERO,
+					  LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
+
+		
+		if (pass == 7)
+		{
+			renderRiggedAlpha(avatarp);
+			return;
+		}
+
+		if (pass == 8)
+		{
+			renderRiggedFullbrightAlpha(avatarp);
+			return;
+		}
+	}
+
+	if (pass == 9)
+	{
+		LLGLEnable blend(GL_BLEND);
+		LLGLDisable test(GL_ALPHA_TEST);
+		gGL.flush();
+
+		LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(-1.0f, -1.0f);
+		gGL.setSceneBlendType(LLRender::BT_ADD);
+
+		LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+		gGL.setColorMask(false, true);
+
+		renderRiggedGlow(avatarp);
+		gGL.setColorMask(true, false);
+		gGL.setSceneBlendType(LLRender::BT_ALPHA);
+		return;
+	}
 	
 	if (sShaderLevel > 0)
 	{
@@ -743,6 +1283,307 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 	}
 }
 
+void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face)
+{
+	LLVector4a* weight = vol_face.mWeights;
+	if (!weight)
+	{
+		return;
+	}
+
+	LLVertexBuffer* buffer = face->getVertexBuffer();
+
+	U32 data_mask = face->getRiggedVertexBufferDataMask();
+	
+	if (!buffer || 
+		buffer->getTypeMask() != data_mask ||
+		buffer->getRequestedVerts() != vol_face.mNumVertices)
+	{
+		face->setGeomIndex(0);
+		face->setIndicesIndex(0);
+		face->setSize(vol_face.mNumVertices, vol_face.mNumIndices, true);
+
+
+		if (sShaderLevel > 0)
+		{
+			buffer = new LLVertexBuffer(data_mask, GL_DYNAMIC_DRAW_ARB);
+		}
+		else
+		{
+			buffer = new LLVertexBuffer(data_mask, GL_STREAM_DRAW_ARB);
+		}
+
+		buffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), true);
+
+		face->setVertexBuffer(buffer);
+
+		U16 offset = 0;
+		
+		LLMatrix4 mat_vert = skin->mBindShapeMatrix;
+		glh::matrix4f m((F32*) mat_vert.mMatrix);
+		m = m.inverse().transpose();
+		
+		F32 mat3[] = 
+		{ m.m[0], m.m[1], m.m[2],
+		  m.m[4], m.m[5], m.m[6],
+		  m.m[8], m.m[9], m.m[10] };
+
+		LLMatrix3 mat_normal(mat3);				
+
+		face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true);
+	}
+
+	if (sShaderLevel <= 0 && face->mLastSkinTime < avatar->getLastSkinTime())
+	{ //perform software vertex skinning for this face
+		LLStrider<LLVector3> position;
+		LLStrider<LLVector3> normal;
+
+		bool has_normal = buffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
+		buffer->getVertexStrider(position);
+
+		if (has_normal)
+		{
+			buffer->getNormalStrider(normal);
+		}
+
+		LLVector4a* pos = (LLVector4a*) position.get();
+
+		LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL;
+		
+		//build matrix palette
+		LLMatrix4a mp[64];
+		LLMatrix4* mat = (LLMatrix4*) mp;
+
+		for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+		{
+			LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
+			if (joint)
+			{
+				mat[j] = skin->mInvBindMatrix[j];
+				mat[j] *= joint->getWorldMatrix();
+			}
+		}
+
+		LLMatrix4a bind_shape_matrix;
+		bind_shape_matrix.loadu(skin->mBindShapeMatrix);
+
+		for (U32 j = 0; j < buffer->getRequestedVerts(); ++j)
+		{
+			LLMatrix4a final_mat;
+			final_mat.clear();
+
+			S32 idx[4];
+
+			LLVector4 wght;
+
+			F32 scale = 0.f;
+			for (U32 k = 0; k < 4; k++)
+			{
+				F32 w = weight[j][k];
+
+				idx[k] = llclamp((S32) floorf(w), 0, 63);
+				wght[k] = w - floorf(w);
+				scale += wght[k];
+			}
+
+			wght *= 1.f/scale;
+
+			for (U32 k = 0; k < 4; k++)
+			{
+				F32 w = wght[k];
+
+				LLMatrix4a src;
+				src.setMul(mp[idx[k]], w);
+
+				final_mat.add(src);
+			}
+
+			
+			LLVector4a& v = vol_face.mPositions[j];
+			LLVector4a t;
+			LLVector4a dst;
+			bind_shape_matrix.affineTransform(v, t);
+			final_mat.affineTransform(t, dst);
+			pos[j] = dst;
+
+			if (norm)
+			{
+				LLVector4a& n = vol_face.mNormals[j];
+				bind_shape_matrix.rotate(n, t);
+				final_mat.rotate(t, dst);
+				norm[j] = dst;
+			}
+		}
+	}
+}
+
+void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
+{
+	if (avatar->isSelf() && !gAgent.needsRenderAvatar())
+	{
+		return;
+	}
+
+	stop_glerror();
+
+	for (U32 i = 0; i < mRiggedFace[type].size(); ++i)
+	{
+		LLFace* face = mRiggedFace[type][i];
+		LLDrawable* drawable = face->getDrawable();
+		if (!drawable)
+		{
+			continue;
+		}
+
+		LLVOVolume* vobj = drawable->getVOVolume();
+
+		if (!vobj)
+		{
+			continue;
+		}
+
+		LLVolume* volume = vobj->getVolume();
+		S32 te = face->getTEOffset();
+
+		if (!volume || volume->getNumVolumeFaces() <= te)
+		{
+			continue;
+		}
+
+		LLUUID mesh_id = volume->getParams().getSculptID();
+		if (mesh_id.isNull())
+		{
+			continue;
+		}
+
+		const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id);
+		if (!skin)
+		{
+			continue;
+		}
+
+		stop_glerror();
+
+		const LLVolumeFace& vol_face = volume->getVolumeFace(te);
+		updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face);
+		
+		stop_glerror();
+
+		U32 data_mask = LLFace::getRiggedDataMask(type);
+
+		LLVertexBuffer* buff = face->getVertexBuffer();
+
+		if (buff)
+		{
+			if (sShaderLevel > 0)
+			{ //upload matrix palette to shader
+				LLMatrix4 mat[64];
+
+				for (U32 i = 0; i < skin->mJointNames.size(); ++i)
+				{
+					LLJoint* joint = avatar->getJoint(skin->mJointNames[i]);
+					if (joint)
+					{
+						mat[i] = skin->mInvBindMatrix[i];
+						mat[i] *= joint->getWorldMatrix();
+					}
+				}
+				
+				stop_glerror();
+
+				LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv("matrixPalette", 
+					skin->mJointNames.size(),
+					FALSE,
+					(GLfloat*) mat[0].mMatrix);
+				
+				stop_glerror();
+			}
+			else
+			{
+				data_mask &= ~LLVertexBuffer::MAP_WEIGHT4;
+			}
+
+			buff->setBuffer(data_mask);
+
+			U16 start = face->getGeomStart();
+			U16 end = start + face->getGeomCount()-1;
+			S32 offset = face->getIndicesStart();
+			U32 count = face->getIndicesCount();
+
+			if (glow)
+			{
+				glColor4f(0,0,0,face->getTextureEntry()->getGlow());
+			}
+
+			gGL.getTexUnit(sDiffuseChannel)->bind(face->getTexture());
+			if (normal_channel > -1)
+			{
+				LLDrawPoolBump::bindBumpMap(face, normal_channel);
+			}
+
+			if (face->mTextureMatrix)
+			{
+				glMatrixMode(GL_TEXTURE);
+				glLoadMatrixf((F32*) face->mTextureMatrix->mMatrix);
+				buff->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+				glLoadIdentity();
+				glMatrixMode(GL_MODELVIEW);
+			}
+			else
+			{
+				buff->drawRange(LLRender::TRIANGLES, start, end, count, offset);		
+			}
+		}
+	}
+}
+
+void LLDrawPoolAvatar::renderDeferredRiggedSimple(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_DEFERRED_SIMPLE);
+}
+
+void LLDrawPoolAvatar::renderDeferredRiggedBump(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_DEFERRED_BUMP);
+}
+
+void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_SIMPLE);
+}
+
+void LLDrawPoolAvatar::renderRiggedFullbright(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_FULLBRIGHT);
+}
+
+	
+void LLDrawPoolAvatar::renderRiggedShinySimple(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_SHINY);
+}
+
+void LLDrawPoolAvatar::renderRiggedFullbrightShiny(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_FULLBRIGHT_SHINY);
+}
+
+void LLDrawPoolAvatar::renderRiggedAlpha(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_ALPHA);
+}
+
+void LLDrawPoolAvatar::renderRiggedFullbrightAlpha(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA);
+}
+
+void LLDrawPoolAvatar::renderRiggedGlow(LLVOAvatar* avatar)
+{
+	renderRigged(avatar, RIGGED_GLOW, true);
+}
+
+
 
 //-----------------------------------------------------------------------------
 // getDebugTexture()
@@ -770,6 +1611,50 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const
 	return LLColor3(0.f, 1.f, 0.f);
 }
 
+void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type)
+{
+	if (type >= NUM_RIGGED_PASSES)
+	{
+		llerrs << "Invalid rigged face type." << llendl;
+	}
+
+	if (facep->getRiggedIndex(type) != -1)
+	{
+		llerrs << "Tried to add a rigged face that's referenced elsewhere." << llendl;
+	}	
+	
+	facep->setRiggedIndex(type, mRiggedFace[type].size());
+	facep->setPool(this);
+	mRiggedFace[type].push_back(facep);
+}
+
+void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep)
+{
+	facep->setPool(NULL);
+
+	for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i)
+	{
+		S32 index = facep->getRiggedIndex(i);
+		
+		if (index > -1)
+		{
+			if (mRiggedFace[i].size() > index && mRiggedFace[i][index] == facep)
+			{
+				facep->setRiggedIndex(i,-1);
+				mRiggedFace[i].erase(mRiggedFace[i].begin()+index);
+				for (U32 j = index; j < mRiggedFace[i].size(); ++j)
+				{ //bump indexes down for faces referenced after erased face
+					mRiggedFace[i][j]->setRiggedIndex(i, j);
+				}
+			}
+			else
+			{
+				llerrs << "Face reference data corrupt for rigged type " << i << llendl;
+			}
+		}
+	}
+}
+
 LLVertexBufferAvatar::LLVertexBufferAvatar()
 : LLVertexBuffer(sDataMask, 
 	GL_STREAM_DRAW_ARB) //avatars are always stream draw due to morph targets
@@ -782,22 +1667,25 @@ void LLVertexBufferAvatar::setupVertexBuffer(U32 data_mask) const
 {
 	if (sRenderingSkinned)
 	{
-		U8* base = useVBOs() ? NULL : mMappedData;
+		U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
 
-		glVertexPointer(3,GL_FLOAT, mStride, (void*)(base + 0));
-		glNormalPointer(GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_NORMAL]));
-		glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD0]));
+		glVertexPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], (void*)(base + 0));
+		glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL]));
+		glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
 		
-		set_vertex_weights(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT], mStride, (F32*)(base + mOffsets[TYPE_WEIGHT]));
+		set_vertex_weights(LLDrawPoolAvatar::sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT], 
+						LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_WEIGHT], (F32*)(base + mOffsets[TYPE_WEIGHT]));
 
 		if (sShaderLevel >= LLDrawPoolAvatar::SHADER_LEVEL_BUMP)
 		{
-			set_binormals(sVertexProgram->mAttribute[LLViewerShaderMgr::BINORMAL], mStride, (LLVector3*)(base + mOffsets[TYPE_BINORMAL]));
+			set_binormals(LLDrawPoolAvatar::sVertexProgram->mAttribute[LLViewerShaderMgr::BINORMAL],
+				LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_BINORMAL], (LLVector3*)(base + mOffsets[TYPE_BINORMAL]));
 		}
 	
 		if (sShaderLevel >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH)
 		{
-			set_vertex_clothing_weights(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_CLOTHING], mStride, (LLVector4*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
+			set_vertex_clothing_weights(LLDrawPoolAvatar::sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_CLOTHING], 
+				LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_CLOTHWEIGHT], (LLVector4*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
 		}
 	}
 	else
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index f536d3c9111092a06bf7084208d8dad5ddbb5e74..fcd8294af544e27d8b4a10150590ef7c3c49b5ce 100644
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -1,4 +1,4 @@
-/** 
+ /** 
  * @file lldrawpoolavatar.h
  * @brief LLDrawPoolAvatar class definition
  *
@@ -30,6 +30,12 @@
 #include "lldrawpool.h"
 
 class LLVOAvatar;
+class LLGLSLShader;
+class LLFace;
+class LLMeshSkinInfo;
+class LLVolume;
+class LLVolumeFace;
+
 
 class LLDrawPoolAvatar : public LLFacePool
 {
@@ -83,7 +89,7 @@ class LLDrawPoolAvatar : public LLFacePool
 	void beginRigid();
 	void beginImpostor();
 	void beginSkinned();
-		
+	
 	void endRigid();
 	void endImpostor();
 	void endSkinned();
@@ -95,14 +101,113 @@ class LLDrawPoolAvatar : public LLFacePool
 	void endDeferredImpostor();
 	void endDeferredRigid();
 	void endDeferredSkinned();
+	
+	void beginPostDeferredAlpha();
+	void endPostDeferredAlpha();
+
+	void beginRiggedSimple();
+	void beginRiggedFullbright();
+	void beginRiggedFullbrightShiny();
+	void beginRiggedShinySimple();
+	void beginRiggedAlpha();
+	void beginRiggedFullbrightAlpha();
+	void beginRiggedGlow();
+	void beginDeferredRiggedAlpha();
+
+	void endRiggedSimple();
+	void endRiggedFullbright();
+	void endRiggedFullbrightShiny();
+	void endRiggedShinySimple();
+	void endRiggedAlpha();
+	void endRiggedFullbrightAlpha();
+	void endRiggedGlow();
+	void endDeferredRiggedAlpha();
+
+	void beginDeferredRiggedSimple();
+	void beginDeferredRiggedBump();
+	
+	void endDeferredRiggedSimple();
+	void endDeferredRiggedBump();
 		
+	void updateRiggedFaceVertexBuffer(LLVOAvatar* avatar,
+									  LLFace* facep, 
+									  const LLMeshSkinInfo* skin, 
+									  LLVolume* volume,
+									  const LLVolumeFace& vol_face);
+
+	void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false);
+	void renderRiggedSimple(LLVOAvatar* avatar);
+	void renderRiggedAlpha(LLVOAvatar* avatar);
+	void renderRiggedFullbrightAlpha(LLVOAvatar* avatar);
+	void renderRiggedFullbright(LLVOAvatar* avatar);
+	void renderRiggedShinySimple(LLVOAvatar* avatar);
+	void renderRiggedFullbrightShiny(LLVOAvatar* avatar);
+	void renderRiggedGlow(LLVOAvatar* avatar);
+	void renderDeferredRiggedSimple(LLVOAvatar* avatar);
+	void renderDeferredRiggedBump(LLVOAvatar* avatar);
+
+	typedef enum
+	{
+		RIGGED_SIMPLE = 0,
+		RIGGED_FULLBRIGHT,
+		RIGGED_SHINY,
+		RIGGED_FULLBRIGHT_SHINY,
+		RIGGED_GLOW,
+		RIGGED_ALPHA,
+		RIGGED_FULLBRIGHT_ALPHA,
+		RIGGED_DEFERRED_BUMP,
+		RIGGED_DEFERRED_SIMPLE,
+		NUM_RIGGED_PASSES,
+		RIGGED_UNKNOWN,
+	} eRiggedPass;
+
+	typedef enum
+	{
+		RIGGED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | 
+							 LLVertexBuffer::MAP_NORMAL | 
+							 LLVertexBuffer::MAP_TEXCOORD0 |
+							 LLVertexBuffer::MAP_COLOR |
+							 LLVertexBuffer::MAP_WEIGHT4,
+		RIGGED_FULLBRIGHT_MASK = LLVertexBuffer::MAP_VERTEX | 
+							 LLVertexBuffer::MAP_TEXCOORD0 |
+							 LLVertexBuffer::MAP_COLOR |
+							 LLVertexBuffer::MAP_WEIGHT4,
+		RIGGED_SHINY_MASK = RIGGED_SIMPLE_MASK,
+		RIGGED_FULLBRIGHT_SHINY_MASK = RIGGED_SIMPLE_MASK,							 
+		RIGGED_GLOW_MASK = LLVertexBuffer::MAP_VERTEX | 
+							 LLVertexBuffer::MAP_TEXCOORD0 |
+							 LLVertexBuffer::MAP_WEIGHT4,
+		RIGGED_ALPHA_MASK = RIGGED_SIMPLE_MASK,
+		RIGGED_FULLBRIGHT_ALPHA_MASK = RIGGED_FULLBRIGHT_MASK,
+		RIGGED_DEFERRED_BUMP_MASK = LLVertexBuffer::MAP_VERTEX | 
+							 LLVertexBuffer::MAP_NORMAL | 
+							 LLVertexBuffer::MAP_TEXCOORD0 |
+							 LLVertexBuffer::MAP_BINORMAL |
+							 LLVertexBuffer::MAP_COLOR |
+							 LLVertexBuffer::MAP_WEIGHT4,
+		RIGGED_DEFERRED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | 
+							 LLVertexBuffer::MAP_NORMAL | 
+							 LLVertexBuffer::MAP_TEXCOORD0 |
+							 LLVertexBuffer::MAP_COLOR |
+							 LLVertexBuffer::MAP_WEIGHT4,
+	} eRiggedDataMask;
+
+	void addRiggedFace(LLFace* facep, U32 type);
+	void removeRiggedFace(LLFace* facep); 
+
+	std::vector<LLFace*> mRiggedFace[NUM_RIGGED_PASSES];
+
 	/*virtual*/ LLViewerTexture *getDebugTexture();
 	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
 
 	void renderAvatars(LLVOAvatar *single_avatar, S32 pass = -1); // renders only one avatar if single_avatar is not null.
 
+
 	static BOOL sSkipOpaque;
 	static BOOL sSkipTransparent;
+	static S32 sDiffuseChannel;
+
+	static LLGLSLShader* sVertexProgram;
 };
 
 class LLVertexBufferAvatar : public LLVertexBuffer
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index c987847c6627d33e6199c576769b6d7384d0344f..29b50761d8ebe48304392d4363b9e78c2adab207 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -145,12 +145,7 @@ void LLStandardBumpmap::addstandard()
 // 		llinfos << "Loading bumpmap: " << bump_image_id << " from viewerart" << llendl;
 		gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
 		gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage = 
-			LLViewerTextureManager::getFetchedTexture(LLUUID(bump_image_id),
-										TRUE, 
-										LLViewerTexture::BOOST_NONE, 
-										LLViewerTexture::LOD_TEXTURE,
-										0, 
-										0);									
+			LLViewerTextureManager::getFetchedTexture(LLUUID(bump_image_id));	
 		gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setBoostLevel(LLViewerTexture::BOOST_BUMP) ;
 		gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage->setLoadedCallback(LLBumpImageList::onSourceStandardLoaded, 0, TRUE, FALSE, NULL, NULL );
 		LLStandardBumpmap::sStandardBumpmapCount++;
@@ -334,30 +329,43 @@ void LLDrawPoolBump::beginShiny(bool invisible)
 		sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0;
 	}
 	
-	if (LLPipeline::sUnderWaterRender)
+	if (getVertexShaderLevel() > 0)
 	{
-		shader = &gObjectShinyWaterProgram;
+		if (LLPipeline::sUnderWaterRender)
+		{
+			shader = &gObjectShinyWaterProgram;
+		}
+		else
+		{
+			shader = &gObjectShinyProgram;
+		}
+		shader->bind();
 	}
 	else
 	{
-		shader = &gObjectShinyProgram;
+		shader = NULL;
 	}
 
+	bindCubeMap(shader, mVertexShaderLevel, diffuse_channel, cube_channel, invisible);
+}
+
+//static
+void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible)
+{
 	LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
 	if( cube_map )
 	{
-		if (!invisible && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0 )
+		if (!invisible && shader )
 		{
 			LLMatrix4 mat;
 			mat.initRows(LLVector4(gGLModelView+0),
 						 LLVector4(gGLModelView+4),
 						 LLVector4(gGLModelView+8),
 						 LLVector4(gGLModelView+12));
-			shader->bind();
 			LLVector3 vec = LLVector3(gShinyOrigin) * mat;
 			LLVector4 vec4(vec, gShinyOrigin.mV[3]);
 			shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);			
-			if (mVertexShaderLevel > 1)
+			if (shader_level > 1)
 			{
 				cube_map->setMatrix(1);
 				// Make sure that texture coord generation happens for tex unit 1, as that's the one we use for 
@@ -419,22 +427,16 @@ void LLDrawPoolBump::renderShiny(bool invisible)
 	}
 }
 
-void LLDrawPoolBump::endShiny(bool invisible)
+//static
+void LLDrawPoolBump::unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible)
 {
-	LLFastTimer t(FTM_RENDER_SHINY);
-	if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| 
-		(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
-	{
-		return;
-	}
-
 	LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
 	if( cube_map )
 	{
 		cube_map->disable();
 		cube_map->restoreMatrix();
 
-		if (!invisible && mVertexShaderLevel > 1)
+		if (!invisible && shader_level > 1)
 		{
 			shader->disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
 					
@@ -445,7 +447,6 @@ void LLDrawPoolBump::endShiny(bool invisible)
 					shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 				}
 			}
-			shader->unbind();
 		}
 	}
 	gGL.getTexUnit(diffuse_channel)->disable();
@@ -453,6 +454,22 @@ void LLDrawPoolBump::endShiny(bool invisible)
 
 	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 	gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
+}
+
+void LLDrawPoolBump::endShiny(bool invisible)
+{
+	LLFastTimer t(FTM_RENDER_SHINY);
+	if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| 
+		(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
+	{
+		return;
+	}
+
+	unbindCubeMap(shader, mVertexShaderLevel, diffuse_channel, cube_channel, invisible);
+	if (shader)
+	{
+		shader->unbind();
+	}
 
 	diffuse_channel = -1;
 	cube_channel = 0;
@@ -473,7 +490,7 @@ void LLDrawPoolBump::beginFullbrightShiny()
 	
 	if (LLPipeline::sUnderWaterRender)
 	{
-		shader = &gObjectShinyWaterProgram;
+		shader = &gObjectFullbrightShinyWaterProgram;
 	}
 	else
 	{
@@ -580,18 +597,37 @@ void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL
 // static
 BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel)
 {
-	LLViewerTexture* bump = NULL;
-
 	U8 bump_code = params.mBump;
 
+	return bindBumpMap(bump_code, params.mTexture, params.mVSize, channel);
+}
+
+//static
+BOOL LLDrawPoolBump::bindBumpMap(LLFace* face, S32 channel)
+{
+	const LLTextureEntry* te = face->getTextureEntry();
+	if (te)
+	{
+		U8 bump_code = te->getBumpmap();
+		return bindBumpMap(bump_code, face->getTexture(), face->getVirtualSize(), channel);
+	}
+
+	return FALSE;
+}
+
+//static
+BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsize, S32 channel)
+{
 	//Note: texture atlas does not support bump texture now.
-	LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(params.mTexture) ;
+	LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(texture) ;
 	if(!tex)
 	{
 		//if the texture is not a fetched texture
 		return FALSE;
 	}
 
+	LLViewerTexture* bump = NULL;
+
 	switch( bump_code )
 	{
 	case BE_NO_BUMP:		
@@ -605,7 +641,7 @@ BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel)
 		if( bump_code < LLStandardBumpmap::sStandardBumpmapCount )
 		{
 			bump = gStandardBumpmapList[bump_code].mImage;
-			gBumpImageList.addTextureStats(bump_code, tex->getID(), params.mVSize);
+			gBumpImageList.addTextureStats(bump_code, tex->getID(), vsize);
 		}
 		break;
 	}
@@ -624,7 +660,7 @@ BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel)
 
 		return TRUE;
 	}
-	
+
 	return FALSE;
 }
 
@@ -969,25 +1005,28 @@ LLViewerTexture* LLBumpImageList::getBrightnessDarknessImage(LLViewerFetchedText
 		}
 
 		bump_image_map_t::iterator iter = entries_list->find(src_image->getID());
-		if (iter != entries_list->end())
+		if (iter != entries_list->end() && iter->second.notNull())
 		{
 			bump = iter->second;
 		}
 		else
 		{
 			LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
-			raw->clear(0x77, 0x77, 0x77, 0xFF);
+			raw->clear(0x77, 0x77, 0xFF, 0xFF);
 
 			(*entries_list)[src_image->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE);
-			(*entries_list)[src_image->getID()]->setExplicitFormat(GL_ALPHA8, GL_ALPHA);			
-
-			// Note: this may create an LLImageGL immediately
-			src_image->setBoostLevel(LLViewerTexture::BOOST_BUMP) ;
-			src_image->setLoadedCallback( callback_func, 0, TRUE, FALSE, new LLUUID(src_image->getID()), NULL );
 			bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image
+		}
 
-//			bump_total++;
-//			llinfos << "*** Creating " << (void*)bump << " " << bump_total << llendl;
+		if (!src_image->hasCallbacks())
+		{ //if image has no callbacks but resolutions don't match, trigger raw image loaded callback again
+			if (src_image->getWidth() != bump->getWidth() ||
+				src_image->getHeight() != bump->getHeight() ||
+				(LLPipeline::sRenderDeferred && bump->getComponents() != 4))
+			{
+				src_image->setBoostLevel(LLViewerTexture::BOOST_BUMP) ;
+				src_image->setLoadedCallback( callback_func, 0, TRUE, FALSE, new LLUUID(src_image->getID()), NULL );
+			}
 		}
 	}
 
@@ -1090,7 +1129,21 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI
 	{
 		bump_image_map_t& entries_list(bump_code == BE_BRIGHTNESS ? gBumpImageList.mBrightnessEntries : gBumpImageList.mDarknessEntries );
 		bump_image_map_t::iterator iter = entries_list.find(source_asset_id);
-		if (iter != entries_list.end()) // bump not cached yet
+
+		if (iter == entries_list.end() ||
+			iter->second.isNull() ||
+                        iter->second->getWidth() != src->getWidth() ||
+                        iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution
+		{ //make sure an entry exists for this image
+			LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
+			raw->clear(0x77, 0x77, 0xFF, 0xFF);
+
+			entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE);
+			iter = entries_list.find(src_vi->getID());
+		}
+
+		//if (iter->second->getWidth() != src->getWidth() ||
+		//	iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution
 		{
 			LLPointer<LLImageRaw> dst_image = new LLImageRaw(src->getWidth(), src->getHeight(), 1);
 			U8* dst_data = dst_image->getData();
@@ -1216,18 +1269,10 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI
 				bump->setExplicitFormat(GL_RGBA, GL_RGBA);
 				bump->createGLTexture(0, nrm_image);
 			}
-
-			
+		
 			iter->second = bump; // derefs (and deletes) old image
 			//---------------------------------------------------
 		}
-		else
-		{
-			// entry should have been added in LLBumpImageList::getImage().
-
-			// Not a legit assertion - the bump texture could have been flushed by the bump image manager
-			//llassert(0);
-		}
 	}
 }
 
diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h
index 65a813ab9482c123defb5314ccd996176564e23f..f4702bf61d4ed3ed8d8a2c6545cc36f0fcdaf64a 100644
--- a/indra/newview/lldrawpoolbump.h
+++ b/indra/newview/lldrawpoolbump.h
@@ -35,6 +35,7 @@
 class LLImageRaw;
 class LLSpatialGroup;
 class LLDrawInfo;
+class LLGLSLShader;
 class LLViewerFetchedTexture;
 
 class LLDrawPoolBump : public LLRenderPass
@@ -73,6 +74,9 @@ protected :
 	void renderBump(U32 pass = LLRenderPass::PASS_BUMP);
 	void endBump(U32 pass = LLRenderPass::PASS_BUMP);
 
+	static void bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible);
+	static void unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible);
+
 	virtual S32 getNumDeferredPasses();
 	/*virtual*/ void beginDeferredPass(S32 pass);
 	/*virtual*/ void endDeferredPass(S32 pass);
@@ -83,7 +87,12 @@ protected :
 	/*virtual*/ void endPostDeferredPass(S32 pass);
 	/*virtual*/ void renderPostDeferred(S32 pass);
 
-	BOOL bindBumpMap(LLDrawInfo& params, S32 channel = -2);
+	static BOOL bindBumpMap(LLDrawInfo& params, S32 channel = -2);
+	static BOOL bindBumpMap(LLFace* face, S32 channel = -2);
+
+private:
+	static BOOL bindBumpMap(U8 bump_code, LLViewerTexture* tex, F32 vsize, S32 channel);
+
 };
 
 enum EBumpEffect
diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp
index 6b45c5abb0a57c7aa497ec64416e09b7c76d128c..030d6e11107369865b0699351e70bbf4d530c512 100644
--- a/indra/newview/lldrawpoolsky.cpp
+++ b/indra/newview/lldrawpoolsky.cpp
@@ -63,6 +63,8 @@ void LLDrawPoolSky::prerender()
 
 void LLDrawPoolSky::render(S32 pass)
 {
+	gGL.flush();
+
 	if (mDrawFace.empty())
 	{
 		return;
@@ -111,13 +113,14 @@ void LLDrawPoolSky::render(S32 pass)
 
 	S32 face_count = (S32)mDrawFace.size();
 
+	LLVertexBuffer::unbind();
+	glColor4f(1,1,1,1);
+
 	for (S32 i = 0; i < llmin(6, face_count); ++i)
 	{
 		renderSkyCubeFace(i);
 	}
 
-	LLGLEnable blend(GL_BLEND);
-
 	glPopMatrix();
 }
 
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
index f1198c9a8de4550a686b510efa2bdbc915e79fce..195ee60a2ef164019287c4ccc860d209e0f3a9a7 100644
--- a/indra/newview/lldrawpooltree.cpp
+++ b/indra/newview/lldrawpooltree.cpp
@@ -107,11 +107,12 @@ void LLDrawPoolTree::render(S32 pass)
 			 iter != mDrawFace.end(); iter++)
 		{
 			LLFace *face = *iter;
-			if(face->mVertexBuffer.notNull())
+			LLVertexBuffer* buff = face->getVertexBuffer();
+			if(buff)
 			{
-				face->mVertexBuffer->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK);
-				face->mVertexBuffer->drawRange(LLRender::TRIANGLES, 0, face->mVertexBuffer->getRequestedVerts()-1, face->mVertexBuffer->getRequestedIndices(), 0); 
-				gPipeline.addTrianglesDrawn(face->mVertexBuffer->getRequestedIndices());
+				buff->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK);
+				buff->drawRange(LLRender::TRIANGLES, 0, buff->getRequestedVerts()-1, buff->getRequestedIndices(), 0); 
+				gPipeline.addTrianglesDrawn(buff->getRequestedIndices());
 			}
 		}
 	}
@@ -200,13 +201,13 @@ void LLDrawPoolTree::renderTree(BOOL selecting)
 		LLFace *face = *iter;
 		LLDrawable *drawablep = face->getDrawable();
 
-		if (drawablep->isDead() || face->mVertexBuffer.isNull())
+		if (drawablep->isDead() || !face->getVertexBuffer())
 		{
 			continue;
 		}
 
-		face->mVertexBuffer->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK);
-		U16* indicesp = (U16*) face->mVertexBuffer->getIndicesPointer();
+		face->getVertexBuffer()->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK);
+		U16* indicesp = (U16*) face->getVertexBuffer()->getIndicesPointer();
 
 		// Render each of the trees
 		LLVOTree *treep = (LLVOTree *)drawablep->getVObj().get();
diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp
index eaa6aa7e37cbacc6199f5f31736e0b2661912a14..696c2d1abdcf3533f291c2193fdbaa4921d222b6 100644
--- a/indra/newview/lldrawpoolwlsky.cpp
+++ b/indra/newview/lldrawpoolwlsky.cpp
@@ -192,7 +192,7 @@ void LLDrawPoolWLSky::renderSkyClouds(F32 camHeightLocal) const
 				&gWLCloudProgram;
 
 		LLGLEnable blend(GL_BLEND);
-		LLGLSBlendFunc blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		gGL.setSceneBlendType(LLRender::BT_ALPHA);
 		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 
 		gGL.getTexUnit(0)->bind(sCloudNoiseTexture);
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index fe201a6773f754bdebe2af2469fe0a397fd47423..5398c13c44086aca667eabfac288df518b749807 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -33,8 +33,10 @@
 #include "llviewercontrol.h"
 #include "llvolume.h"
 #include "m3math.h"
+#include "llmatrix4a.h"
 #include "v3color.h"
 
+#include "lldrawpoolavatar.h"
 #include "lldrawpoolbump.h"
 #include "llgl.h"
 #include "llrender.h"
@@ -67,35 +69,43 @@ The resulting texture coordinate <u,v> is:
 	u = 2(B dot P)
 	v = 2(T dot P)
 */
-void planarProjection(LLVector2 &tc, const LLVector3& normal,
-					  const LLVector3 &mCenter, const LLVector3& vec)
-{	//DONE!
-	LLVector3 binormal;
-	float d = normal * LLVector3(1,0,0);
+void planarProjection(LLVector2 &tc, const LLVector4a& normal,
+					  const LLVector4a &center, const LLVector4a& vec)
+{	
+	LLVector4a binormal;
+	F32 d = normal[0];
+
 	if (d >= 0.5f || d <= -0.5f)
 	{
-		binormal = LLVector3(0,1,0);
-		if (normal.mV[0] < 0)
+		if (d < 0)
+		{
+			binormal.set(0,-1,0);
+		}
+		else
 		{
-			binormal = -binormal;
+			binormal.set(0, 1, 0);
 		}
 	}
 	else
 	{
-        binormal = LLVector3(1,0,0);
-		if (normal.mV[1] > 0)
+        if (normal[1] > 0)
 		{
-			binormal = -binormal;
+			binormal.set(-1,0,0);
+		}
+		else
+		{
+			binormal.set(1,0,0);
 		}
 	}
-	LLVector3 tangent = binormal % normal;
+	LLVector4a tangent;
+	tangent.setCross3(binormal,normal);
 
-	tc.mV[1] = -((tangent*vec)*2 - 0.5f);
-	tc.mV[0] = 1.0f+((binormal*vec)*2 - 0.5f);
+	tc.mV[1] = -((tangent.dot3(vec).getF32())*2 - 0.5f);
+	tc.mV[0] = 1.0f+((binormal.dot3(vec).getF32())*2 - 0.5f);
 }
 
-void sphericalProjection(LLVector2 &tc, const LLVector3& normal,
-						 const LLVector3 &mCenter, const LLVector3& vec)
+void sphericalProjection(LLVector2 &tc, const LLVector4a& normal,
+						 const LLVector4a &mCenter, const LLVector4a& vec)
 {	//BROKEN
 	/*tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/3.14159f;
 	
@@ -106,7 +116,7 @@ void sphericalProjection(LLVector2 &tc, const LLVector3& normal,
 	}*/
 }
 
-void cylindricalProjection(LLVector2 &tc, const LLVector3& normal, const LLVector3 &mCenter, const LLVector3& vec)
+void cylindricalProjection(LLVector2 &tc, const LLVector4a& normal, const LLVector4a &mCenter, const LLVector4a& vec)
 {	//BROKEN
 	/*LLVector3 binormal;
 	float d = vd.mNormal * LLVector3(1,0,0);
@@ -138,6 +148,7 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
 {
 	mLastUpdateTime = gFrameTimeSeconds;
 	mLastMoveTime = 0.f;
+	mLastSkinTime = gFrameTimeSeconds;
 	mVSize = 0.f;
 	mPixelArea = 16.f;
 	mState      = GLOBAL;
@@ -179,9 +190,13 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
 	mHasMedia = FALSE ;
 }
 
-
 void LLFace::destroy()
 {
+	if (gDebugGL)
+	{
+		gPipeline.checkReferences(this);
+	}
+
 	if(mTexture.notNull())
 	{
 		mTexture->removeFace(this) ;
@@ -189,7 +204,15 @@ void LLFace::destroy()
 	
 	if (mDrawPoolp)
 	{
-		mDrawPoolp->removeFace(this);
+		if (this->isState(LLFace::RIGGED) && mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR)
+		{
+			((LLDrawPoolAvatar*) mDrawPoolp)->removeRiggedFace(this);
+		}
+		else
+		{
+			mDrawPoolp->removeFace(this);
+		}
+	
 		mDrawPoolp = NULL;
 	}
 
@@ -210,8 +233,8 @@ void LLFace::destroy()
 	}
 	
 	setDrawInfo(NULL);
-	
 	removeAtlas();
+		
 	mDrawablep = NULL;
 	mVObjp = NULL;
 }
@@ -227,6 +250,11 @@ void LLFace::setWorldMatrix(const LLMatrix4 &mat)
 	llerrs << "Faces on this drawable are not independently modifiable\n" << llendl;
 }
 
+void LLFace::setPool(LLFacePool* pool)
+{
+	mDrawPoolp = pool;
+}
+
 void LLFace::setPool(LLFacePool* new_pool, LLViewerTexture *texturep)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
@@ -329,8 +357,21 @@ void LLFace::setDrawable(LLDrawable *drawable)
 	mXform      = &drawable->mXform;
 }
 
-void LLFace::setSize(const S32 num_vertices, const S32 num_indices)
+void LLFace::setSize(S32 num_vertices, S32 num_indices, bool align)
 {
+	if (align)
+	{
+		//allocate vertices in blocks of 4 for alignment
+		num_vertices = (num_vertices + 0x3) & ~0x3;
+	}
+	else
+	{
+		if (mDrawablep->getVOVolume())
+		{
+			llerrs << "WTF?" << llendl;
+		}
+	}
+
 	if (mGeomCount != num_vertices ||
 		mIndicesCount != num_indices)
 	{
@@ -339,8 +380,28 @@ void LLFace::setSize(const S32 num_vertices, const S32 num_indices)
 		mVertexBuffer = NULL;
 		mLastVertexBuffer = NULL;
 	}
+
+	llassert(verify());
+}
+
+void LLFace::setGeomIndex(U16 idx) 
+{ 
+	if (mGeomIndex != idx)
+	{
+		mGeomIndex = idx; 
+		mVertexBuffer = NULL;
+	}
 }
 
+void LLFace::setIndicesIndex(S32 idx) 
+{ 
+	if (mIndicesIndex != idx)
+	{
+		mIndicesIndex = idx; 
+		mVertexBuffer = NULL;
+	}
+}
+	
 //============================================================================
 
 U16 LLFace::getGeometryAvatar(
@@ -429,8 +490,36 @@ void LLFace::renderSelected(LLViewerTexture *imagep, const LLColor4& color)
 		}
 
 		glColor4fv(color.mV);
-		mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
-		mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex);
+	
+		if (mDrawablep->isState(LLDrawable::RIGGED))
+		{
+			LLVOVolume* volume = mDrawablep->getVOVolume();
+			if (volume)
+			{
+				LLRiggedVolume* rigged = volume->getRiggedVolume();
+				if (rigged)
+				{
+					LLGLEnable offset(GL_POLYGON_OFFSET_FILL);
+					glPolygonOffset(-1.f, -1.f);
+					glMultMatrixf((F32*) volume->getRelativeXform().mMatrix);
+					const LLVolumeFace& vol_face = rigged->getVolumeFace(getTEOffset());
+					LLVertexBuffer::unbind();
+					glVertexPointer(3, GL_FLOAT, 16, vol_face.mPositions);
+					if (vol_face.mTexCoords)
+					{
+						glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+						glTexCoordPointer(2, GL_FLOAT, 8, vol_face.mTexCoords);
+					}
+					glDrawElements(GL_TRIANGLES, vol_face.mNumIndices, GL_UNSIGNED_SHORT, vol_face.mIndices);
+					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+				}
+			}
+		}
+		else
+		{
+			mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
+			mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex);
+		}
 
 		gGL.popMatrix();
 	}
@@ -515,23 +604,26 @@ void LLFace::printDebugInfo() const
 	llinfos << "II: " << mIndicesIndex << " Count:" << mIndicesCount << llendl;
 	llinfos << llendl;
 
-	poolp->printDebugInfo();
-
-	S32 pool_references = 0;
-	for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
-		 iter != poolp->mReferences.end(); iter++)
+	if (poolp)
 	{
-		LLFace *facep = *iter;
-		if (facep == this)
+		poolp->printDebugInfo();
+
+		S32 pool_references = 0;
+		for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
+			 iter != poolp->mReferences.end(); iter++)
 		{
-			llinfos << "Pool reference: " << pool_references << llendl;
-			pool_references++;
+			LLFace *facep = *iter;
+			if (facep == this)
+			{
+				llinfos << "Pool reference: " << pool_references << llendl;
+				pool_references++;
+			}
 		}
-	}
 
-	if (pool_references != 1)
-	{
-		llinfos << "Incorrect number of pool references!" << llendl;
+		if (pool_references != 1)
+		{
+			llinfos << "Incorrect number of pool references!" << llendl;
+		}
 	}
 
 #if 0
@@ -588,95 +680,116 @@ static void xform(LLVector2 &tex_coord, F32 cosAng, F32 sinAng, F32 offS, F32 of
 
 
 BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
-								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL global_volume)
+								const LLMatrix4& mat_vert_in, const LLMatrix3& mat_normal_in, BOOL global_volume)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
 
 	//get bounding box
-	if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION))
+	if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED))
 	{
+		//VECTORIZE THIS
+		LLMatrix4a mat_vert;
+		mat_vert.loadu(mat_vert_in);
+
+		LLMatrix4a mat_normal;
+		mat_normal.loadu(mat_normal_in);
+
 		//if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
 		//{ //vertex buffer no longer valid
 		//	mVertexBuffer = NULL;
 		//	mLastVertexBuffer = NULL;
 		//}
 
-		LLVector3 min,max;
+		//VECTORIZE THIS
+		LLVector4a min,max;
 	
 		if (f >= volume.getNumVolumeFaces())
 		{
-			min = LLVector3(-1,-1,-1);
-			max = LLVector3(1,1,1);
-		}
-		else
-		{
-			const LLVolumeFace &face = volume.getVolumeFace(f);
-			min = face.mExtents[0];
-			max = face.mExtents[1];
+			llwarns << "Generating bounding box for invalid face index!" << llendl;
+			f = 0;
 		}
 
+		const LLVolumeFace &face = volume.getVolumeFace(f);
+		min = face.mExtents[0];
+		max = face.mExtents[1];
+		
+
 		//min, max are in volume space, convert to drawable render space
-		LLVector3 center = ((min + max) * 0.5f)*mat_vert;
-		LLVector3 size = ((max-min) * 0.5f);
+		LLVector4a center;
+		LLVector4a t;
+		t.setAdd(min, max);
+		t.mul(0.5f);
+		mat_vert.affineTransform(t, center);
+		LLVector4a size;
+		size.setSub(max, min);
+		size.mul(0.5f);
+
 		if (!global_volume)
 		{
-			size.scaleVec(mDrawablep->getVObj()->getScale());
+			//VECTORIZE THIS
+			LLVector4a scale;
+			scale.load3(mDrawablep->getVObj()->getScale().mV);
+			size.mul(scale);
 		}
 
-		LLMatrix3 mat = mat_normal;
-		LLVector3 x = mat.getFwdRow();
-		LLVector3 y = mat.getLeftRow();
-		LLVector3 z = mat.getUpRow();
-		x.normVec();
-		y.normVec();
-		z.normVec();
+		mat_normal.mMatrix[0].normalize3fast();
+		mat_normal.mMatrix[1].normalize3fast();
+		mat_normal.mMatrix[2].normalize3fast();
+		
+		LLVector4a v[4];
 
-		mat.setRows(x,y,z);
+		//get 4 corners of bounding box
+		mat_normal.rotate(size,v[0]);
 
-		LLQuaternion rotation = LLQuaternion(mat);
+		//VECTORIZE THIS
+		LLVector4a scale;
 		
-		LLVector3 v[4];
-		//get 4 corners of bounding box
-		v[0] = (size * rotation);
-		v[1] = (LLVector3(-size.mV[0], -size.mV[1], size.mV[2]) * rotation);
-		v[2] = (LLVector3(size.mV[0], -size.mV[1], -size.mV[2]) * rotation);
-		v[3] = (LLVector3(-size.mV[0], size.mV[1], -size.mV[2]) * rotation);
+		scale.set(-1.f, -1.f, 1.f);
+		scale.mul(size);
+		mat_normal.rotate(scale, v[1]);
+		
+		scale.set(1.f, -1.f, -1.f);
+		scale.mul(size);
+		mat_normal.rotate(scale, v[2]);
+		
+		scale.set(-1.f, 1.f, -1.f);
+		scale.mul(size);
+		mat_normal.rotate(scale, v[3]);
 
-		LLVector3& newMin = mExtents[0];
-		LLVector3& newMax = mExtents[1];
+		LLVector4a& newMin = mExtents[0];
+		LLVector4a& newMax = mExtents[1];
 		
 		newMin = newMax = center;
 		
 		for (U32 i = 0; i < 4; i++)
 		{
-			for (U32 j = 0; j < 3; j++)
-			{
-				F32 delta = fabsf(v[i].mV[j]);
-				F32 min = center.mV[j] - delta;
-				F32 max = center.mV[j] + delta;
-				
-				if (min < newMin.mV[j])
-				{
-					newMin.mV[j] = min;
-				}
-				
-				if (max > newMax.mV[j])
-				{
-					newMax.mV[j] = max;
-				}
-			}
+			LLVector4a delta;
+			delta.setAbs(v[i]);
+			LLVector4a min;
+			min.setSub(center, delta);
+			LLVector4a max;
+			max.setAdd(center, delta);
+
+			newMin.setMin(newMin,min);
+			newMax.setMax(newMax,max);
 		}
 
 		if (!mDrawablep->isActive())
 		{
-			LLVector3 offset = mDrawablep->getRegion()->getOriginAgent();
-			newMin += offset;
-			newMax += offset;
+			LLVector4a offset;
+			offset.load3(mDrawablep->getRegion()->getOriginAgent().mV);
+			newMin.add(offset);
+			newMax.add(offset);
 		}
 
-		mCenterLocal = (newMin+newMax)*0.5f;
-		LLVector3 tmp = (newMin - newMax) ;
-		mBoundingSphereRadius = tmp.length() * 0.5f ;
+		t.setAdd(newMin, newMax);
+		t.mul(0.5f);
+
+		//VECTORIZE THIS
+		mCenterLocal.set(t.getF32ptr());
+		
+		t.setSub(newMax,newMin);
+		mBoundingSphereRadius = t.getLength3().getF32()*0.5f;
 
 		updateCenterAgent();
 	}
@@ -703,18 +816,26 @@ LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, LLVector3 position,
 		return surface_coord;
 	}
 
+	//VECTORIZE THIS
 	// see if we have a non-default mapping
     U8 texgen = getTextureEntry()->getTexGen();
 	if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
 	{
-		LLVector3 center = mDrawablep->getVOVolume()->getVolume()->getVolumeFace(mTEOffset).mCenter;
+		LLVector4a& center = *(mDrawablep->getVOVolume()->getVolume()->getVolumeFace(mTEOffset).mCenter);
+		
+		LLVector4a volume_position;
+		volume_position.load3(mDrawablep->getVOVolume()->agentPositionToVolume(position).mV);
 		
-		LLVector3 scale  = (mDrawablep->getVOVolume()->isVolumeGlobal()) ? LLVector3(1,1,1) : mVObjp->getScale();
-		LLVector3 volume_position = mDrawablep->getVOVolume()->agentPositionToVolume(position);
-		volume_position.scaleVec(scale);
+		if (!mDrawablep->getVOVolume()->isVolumeGlobal())
+		{
+			LLVector4a scale;
+			scale.load3(mVObjp->getScale().mV);
+			volume_position.mul(scale);
+		}
 		
-		LLVector3 volume_normal   = mDrawablep->getVOVolume()->agentDirectionToVolume(normal);
-		volume_normal.normalize();
+		LLVector4a volume_normal;
+		volume_normal.load3(mDrawablep->getVOVolume()->agentDirectionToVolume(normal).mV);
+		volume_normal.normalize3fast();
 		
 		switch (texgen)
 		{
@@ -755,10 +876,10 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po
 {
 	const LLMatrix4& vol_mat = getWorldMatrix();
 	const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset);
-	LLVector3 normal = vf.mVertices[0].mNormal;
-	LLVector3 binormal = vf.mVertices[0].mBinormal;
+	const LLVector4a& normal4a = vf.mNormals[0];
+	const LLVector4a& binormal4a = vf.mBinormals[0];
 	LLVector2 projected_binormal;
-	planarProjection(projected_binormal, normal, vf.mCenter, binormal);
+	planarProjection(projected_binormal, normal4a, *vf.mCenter, binormal4a);
 	projected_binormal -= LLVector2(0.5f, 0.5f); // this normally happens in xform()
 	*scale = projected_binormal.length();
 	// rotate binormal to match what planarProjection() thinks it is,
@@ -766,6 +887,10 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po
 	projected_binormal.normalize();
 	F32 ang = acos(projected_binormal.mV[VY]);
 	ang = (projected_binormal.mV[VX] < 0.f) ? -ang : ang;
+
+	//VECTORIZE THIS
+	LLVector3 binormal(binormal4a.getF32ptr());
+	LLVector3 normal(normal4a.getF32ptr());
 	binormal.rotVec(ang, normal);
 	LLQuaternion local_rot( binormal % normal, binormal, normal );
 	*face_rot = local_rot * vol_mat.quaternion();
@@ -848,20 +973,35 @@ void LLFace::updateRebuildFlags()
 
 bool LLFace::canRenderAsMask()
 {
+	if (LLPipeline::sNoAlpha)
+	{
+		return true;
+	}
+
 	const LLTextureEntry* te = getTextureEntry();
-	return (
-		(
-		 (LLPipeline::sRenderDeferred && LLPipeline::sAutoMaskAlphaDeferred) ||
-		 
-		 (!LLPipeline::sRenderDeferred && LLPipeline::sAutoMaskAlphaNonDeferred)		 
-		 ) // do we want masks at all?
-		&&
-		(te->getColor().mV[3] == 1.0f) && // can't treat as mask if we have face alpha
-		!(LLPipeline::sRenderDeferred && te->getFullbright()) && // hack: alpha masking renders fullbright faces invisible in deferred rendering mode, need to figure out why - for now, avoid
+
+	if ((te->getColor().mV[3] == 1.0f) && // can't treat as mask if we have face alpha
 		(te->getGlow() == 0.f) && // glowing masks are hard to implement - don't mask
+		getTexture()->getIsAlphaMask()) // texture actually qualifies for masking (lazily recalculated but expensive)
+	{
+		if (LLPipeline::sRenderDeferred)
+		{
+			if (getViewerObject()->isHUDAttachment() || te->getFullbright())
+			{ //hud attachments and fullbright objects are NOT subject to the deferred rendering pipe
+				return LLPipeline::sAutoMaskAlphaNonDeferred;
+			}
+			else
+			{
+				return LLPipeline::sAutoMaskAlphaDeferred;
+			}
+		}
+		else
+		{
+			return LLPipeline::sAutoMaskAlphaNonDeferred;
+		}
+	}
 
-		getTexture()->getIsAlphaMask() // texture actually qualifies for masking (lazily recalculated but expensive)
-		);
+	return false;
 }
 
 
@@ -869,13 +1009,15 @@ static LLFastTimer::DeclareTimer FTM_FACE_GET_GEOM("Face Geom");
 
 BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 							   const S32 &f,
-								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
-								const U16 &index_offset)
+								const LLMatrix4& mat_vert_in, const LLMatrix3& mat_norm_in,
+								const U16 &index_offset,
+								bool force_rebuild)
 {
 	LLFastTimer t(FTM_FACE_GET_GEOM);
+	llassert(verify());
 	const LLVolumeFace &vf = volume.getVolumeFace(f);
-	S32 num_vertices = (S32)vf.mVertices.size();
-	S32 num_indices = LLPipeline::sUseTriStrips ? (S32)vf.mTriStrip.size() : (S32) vf.mIndices.size();
+	S32 num_vertices = (S32)vf.mNumVertices;
+	S32 num_indices = (S32) vf.mNumIndices;
 	
 	if (mVertexBuffer.notNull())
 	{
@@ -900,15 +1042,20 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		}
 	}
 
-	LLStrider<LLVector3> vertices;
+	LLStrider<LLVector3> vert;
+	LLVector4a* vertices = NULL;
 	LLStrider<LLVector2> tex_coords;
 	LLStrider<LLVector2> tex_coords2;
-	LLStrider<LLVector3> normals;
+	LLVector4a* normals = NULL;
+	LLStrider<LLVector3> norm;
 	LLStrider<LLColor4U> colors;
-	LLStrider<LLVector3> binormals;
+	LLVector4a* binormals = NULL;
+	LLStrider<LLVector3> binorm;
 	LLStrider<U16> indicesp;
+	LLVector4a* weights = NULL;
+	LLStrider<LLVector4> wght;
 
-	BOOL full_rebuild = mDrawablep->isState(LLDrawable::REBUILD_VOLUME);
+	BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME);
 	
 	BOOL global_volume = mDrawablep->getVOVolume()->isVolumeGlobal();
 	LLVector3 scale;
@@ -921,28 +1068,37 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		scale = mVObjp->getScale();
 	}
 	
-	BOOL rebuild_pos = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_POSITION);
-	BOOL rebuild_color = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_COLOR);
-	BOOL rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD);
-	BOOL rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
-	BOOL rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL);
+	bool rebuild_pos = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_POSITION);
+	bool rebuild_color = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_COLOR);
+	bool rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD);
+	bool rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
+	bool rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL);
+	bool rebuild_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4);
 
 	const LLTextureEntry *tep = mVObjp->getTE(f);
-	U8  bump_code = tep ? tep->getBumpmap() : 0;
+	const U8 bump_code = tep ? tep->getBumpmap() : 0;
 
 	if (rebuild_pos)
 	{
-		mVertexBuffer->getVertexStrider(vertices, mGeomIndex);
+		mVertexBuffer->getVertexStrider(vert, mGeomIndex);
+		vertices = (LLVector4a*) vert.get();
 	}
 	if (rebuild_normal)
 	{
-		mVertexBuffer->getNormalStrider(normals, mGeomIndex);
+		mVertexBuffer->getNormalStrider(norm, mGeomIndex);
+		normals = (LLVector4a*) norm.get();
 	}
 	if (rebuild_binormal)
 	{
-		mVertexBuffer->getBinormalStrider(binormals, mGeomIndex);
+		mVertexBuffer->getBinormalStrider(binorm, mGeomIndex);
+		binormals = (LLVector4a*) binorm.get();
 	}
-
+	if (rebuild_weights)
+	{
+		mVertexBuffer->getWeight4Strider(wght, mGeomIndex);
+		weights = (LLVector4a*) wght.get();
+	}
+	
 	F32 tcoord_xoffset = 0.f ;
 	F32 tcoord_yoffset = 0.f ;
 	F32 tcoord_xscale = 1.f ;
@@ -974,8 +1130,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		mVertexBuffer->getColorStrider(colors, mGeomIndex);
 	}
 
-	F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0;
-	
 	BOOL is_static = mDrawablep->isStatic();
 	BOOL is_global = is_static;
 
@@ -990,59 +1144,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		clearState(GLOBAL);
 	}
 
-	LLVector2 tmin, tmax;
-	
-	
-
-	if (rebuild_tcoord)
-	{
-		if (tep)
-		{
-			r  = tep->getRotation();
-			os = tep->mOffsetS;
-			ot = tep->mOffsetT;
-			ms = tep->mScaleS;
-			mt = tep->mScaleT;
-			cos_ang = cos(r);
-			sin_ang = sin(r);
-		}
-		else
-		{
-			cos_ang = 1.0f;
-			sin_ang = 0.0f;
-			os = 0.0f;
-			ot = 0.0f;
-			ms = 1.0f;
-			mt = 1.0f;
-		}
-	}
-
-	U8 tex_mode = 0;
-	
-	if (isState(TEXTURE_ANIM))
-	{
-		LLVOVolume* vobj = (LLVOVolume*) (LLViewerObject*) mVObjp;	
-		tex_mode = vobj->mTexAnimMode;
-
-		if (!tex_mode)
-		{
-			clearState(TEXTURE_ANIM);
-		}
-		else
-		{
-			os = ot = 0.f;
-			r = 0.f;
-			cos_ang = 1.f;
-			sin_ang = 0.f;
-			ms = mt = 1.f;
-		}
-
-		if (getVirtualSize() >= MIN_TEX_ANIM_SIZE)
-		{ //don't override texture transform during tc bake
-			tex_mode = 0;
-		}
-	}
-
 	LLColor4U color = tep->getColor();
 
 	if (rebuild_color)
@@ -1064,265 +1165,469 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		}
 	}
 
-    // INDICES
+	// INDICES
 	if (full_rebuild)
 	{
 		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
-		if (LLPipeline::sUseTriStrips)
+		__m128i* dst = (__m128i*) indicesp.get();
+		__m128i* src = (__m128i*) vf.mIndices;
+		__m128i offset = _mm_set1_epi16(index_offset);
+
+		S32 end = num_indices/8;
+		
+		for (S32 i = 0; i < end; i++)
 		{
-			for (U32 i = 0; i < (U32) num_indices; i++)
-			{
-				*indicesp++ = vf.mTriStrip[i] + index_offset;
-			}
+			__m128i res = _mm_add_epi16(src[i], offset);
+			_mm_storeu_si128(dst+i, res);
 		}
-		else
+
+		for (S32 i = end*8; i < num_indices; ++i)
 		{
-			for (U32 i = 0; i < (U32) num_indices; i++)
-			{
-				*indicesp++ = vf.mIndices[i] + index_offset;
-			}
+			indicesp[i] = vf.mIndices[i]+index_offset;
 		}
 	}
 	
+	LLMatrix4a mat_normal;
+	mat_normal.loadu(mat_norm_in);
 	
-	//bump setup
-	LLVector3 binormal_dir( -sin_ang, cos_ang, 0 );
-	LLVector3 bump_s_primary_light_ray;
-	LLVector3 bump_t_primary_light_ray;
+	//if it's not fullbright and has no normals, bake sunlight based on face normal
+	//bool bake_sunlight = !getTextureEntry()->getFullbright() &&
+	//  !mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
 
-	LLQuaternion bump_quat;
-	if (mDrawablep->isActive())
-	{
-		bump_quat = LLQuaternion(mDrawablep->getRenderMatrix());
-	}
-	
-	if (bump_code)
+	F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0;
+
+	if (rebuild_tcoord)
 	{
-		mVObjp->getVolume()->genBinormals(f);
-		F32 offset_multiple; 
-		switch( bump_code )
+		bool do_xform;
+			
+		if (tep)
 		{
-			case BE_NO_BUMP:
-			offset_multiple = 0.f;
-			break;
-			case BE_BRIGHTNESS:
-			case BE_DARKNESS:
-			if( mTexture.notNull() && mTexture->hasGLTexture())
+			r  = tep->getRotation();
+			os = tep->mOffsetS;
+			ot = tep->mOffsetT;
+			ms = tep->mScaleS;
+			mt = tep->mScaleT;
+			cos_ang = cos(r);
+			sin_ang = sin(r);
+
+			if (cos_ang != 1.f || 
+				sin_ang != 0.f ||
+				os != 0.f ||
+				ot != 0.f ||
+				ms != 1.f ||
+				mt != 1.f)
 			{
-				// Offset by approximately one texel
-				S32 cur_discard = mTexture->getDiscardLevel();
-				S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
-				max_size <<= cur_discard;
-				const F32 ARTIFICIAL_OFFSET = 2.f;
-				offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
+				do_xform = true;
 			}
 			else
 			{
-				offset_multiple = 1.f/256;
-			}
-			break;
-
-			default:  // Standard bumpmap textures.  Assumed to be 256x256
-			offset_multiple = 1.f / 256;
-			break;
+				do_xform = false;
+			}	
 		}
-
-		F32 s_scale = 1.f;
-		F32 t_scale = 1.f;
-		if( tep )
+		else
 		{
-			tep->getScale( &s_scale, &t_scale );
+			do_xform = false;
 		}
-		// Use the nudged south when coming from above sun angle, such
-		// that emboss mapping always shows up on the upward faces of cubes when 
-		// it's noon (since a lot of builders build with the sun forced to noon).
-		LLVector3   sun_ray  = gSky.mVOSkyp->mBumpSunDir;
-		LLVector3   moon_ray = gSky.getMoonDirection();
-		LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray;
+						
+		//bump setup
+		LLVector4a binormal_dir( -sin_ang, cos_ang, 0.f );
+		LLVector4a bump_s_primary_light_ray(0.f, 0.f, 0.f);
+		LLVector4a bump_t_primary_light_ray(0.f, 0.f, 0.f);
 
-		bump_s_primary_light_ray = offset_multiple * s_scale * primary_light_ray;
-		bump_t_primary_light_ray = offset_multiple * t_scale * primary_light_ray;
-	}
+		LLQuaternion bump_quat;
+		if (mDrawablep->isActive())
+		{
+			bump_quat = LLQuaternion(mDrawablep->getRenderMatrix());
+		}
 		
-	U8 texgen = getTextureEntry()->getTexGen();
-	if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT)
-	{ //planar texgen needs binormals
-		mVObjp->getVolume()->genBinormals(f);
-	}
-
-	for (S32 i = 0; i < num_vertices; i++)
-	{
-		if (rebuild_tcoord)
+		if (bump_code)
 		{
-			LLVector2 tc = vf.mVertices[i].mTexCoord;
-		   
-			if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+			mVObjp->getVolume()->genBinormals(f);
+			F32 offset_multiple; 
+			switch( bump_code )
 			{
-				LLVector3 vec = vf.mVertices[i].mPosition; 
-			
-				vec.scaleVec(scale);
-
-				switch (texgen)
+				case BE_NO_BUMP:
+				offset_multiple = 0.f;
+				break;
+				case BE_BRIGHTNESS:
+				case BE_DARKNESS:
+				if( mTexture.notNull() && mTexture->hasGLTexture())
 				{
-					case LLTextureEntry::TEX_GEN_PLANAR:
-						planarProjection(tc, vf.mVertices[i].mNormal, vf.mCenter, vec);
-						break;
-					case LLTextureEntry::TEX_GEN_SPHERICAL:
-						sphericalProjection(tc, vf.mVertices[i].mNormal, vf.mCenter, vec);
-						break;
-					case LLTextureEntry::TEX_GEN_CYLINDRICAL:
-						cylindricalProjection(tc, vf.mVertices[i].mNormal, vf.mCenter, vec);
-						break;
-					default:
-						break;
-				}		
+					// Offset by approximately one texel
+					S32 cur_discard = mTexture->getDiscardLevel();
+					S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
+					max_size <<= cur_discard;
+					const F32 ARTIFICIAL_OFFSET = 2.f;
+					offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
+				}
+				else
+				{
+					offset_multiple = 1.f/256;
+				}
+				break;
+
+				default:  // Standard bumpmap textures.  Assumed to be 256x256
+				offset_multiple = 1.f / 256;
+				break;
 			}
 
-			if (tex_mode && mTextureMatrix)
+			F32 s_scale = 1.f;
+			F32 t_scale = 1.f;
+			if( tep )
 			{
-				LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f);
-				tmp = tmp * *mTextureMatrix;
-				tc.mV[0] = tmp.mV[0];
-				tc.mV[1] = tmp.mV[1];
+				tep->getScale( &s_scale, &t_scale );
+			}
+			// Use the nudged south when coming from above sun angle, such
+			// that emboss mapping always shows up on the upward faces of cubes when 
+			// it's noon (since a lot of builders build with the sun forced to noon).
+			LLVector3   sun_ray  = gSky.mVOSkyp->mBumpSunDir;
+			LLVector3   moon_ray = gSky.getMoonDirection();
+			LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray;
+
+			bump_s_primary_light_ray.load3((offset_multiple * s_scale * primary_light_ray).mV);
+			bump_t_primary_light_ray.load3((offset_multiple * t_scale * primary_light_ray).mV);
+		}
+
+		U8 texgen = getTextureEntry()->getTexGen();
+		if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+		{ //planar texgen needs binormals
+			mVObjp->getVolume()->genBinormals(f);
+		}
+
+		U8 tex_mode = 0;
+	
+		if (isState(TEXTURE_ANIM))
+		{
+			LLVOVolume* vobj = (LLVOVolume*) (LLViewerObject*) mVObjp;	
+			tex_mode = vobj->mTexAnimMode;
+
+			if (!tex_mode)
+			{
+				clearState(TEXTURE_ANIM);
 			}
 			else
 			{
-				xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+				os = ot = 0.f;
+				r = 0.f;
+				cos_ang = 1.f;
+				sin_ang = 0.f;
+				ms = mt = 1.f;
+
+				do_xform = false;
 			}
 
-			if(in_atlas)
-			{
-				//
-				//manually calculate tex-coord per vertex for varying address modes.
-				//should be removed if shader can handle this.
-				//
+			if (getVirtualSize() >= MIN_TEX_ANIM_SIZE)
+			{ //don't override texture transform during tc bake
+				tex_mode = 0;
+			}
+		}
 
-				S32 int_part = 0 ;
-				switch(mTexture->getAddressMode())
-				{
-				case LLTexUnit::TAM_CLAMP:
-					if(tc.mV[0] < 0.f)
-					{
-						tc.mV[0] = 0.f ;
-					}
-					else if(tc.mV[0] > 1.f)
-					{
-						tc.mV[0] = 1.f;
-					}
+		LLVector4a scalea;
+		scalea.load3(scale.mV);
 
-					if(tc.mV[1] < 0.f)
-					{
-						tc.mV[1] = 0.f ;
-					}
-					else if(tc.mV[1] > 1.f)
-					{
-						tc.mV[1] = 1.f;
-					}
-					break;
-				case LLTexUnit::TAM_MIRROR:
-					if(tc.mV[0] < 0.f)
-					{
-						tc.mV[0] = -tc.mV[0] ;
-					}
-					int_part = (S32)tc.mV[0] ;
-					if(int_part & 1) //odd number
-					{
-						tc.mV[0] = int_part + 1 - tc.mV[0] ;
-					}
-					else //even number
-					{
-						tc.mV[0] -= int_part ;
-					}
+		bool do_bump = bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1);
+		bool do_tex_mat = tex_mode && mTextureMatrix;
 
-					if(tc.mV[1] < 0.f)
+		if (!in_atlas && !do_bump)
+		{ //not in atlas or not bump mapped, might be able to do a cheap update
+			if (texgen != LLTextureEntry::TEX_GEN_PLANAR)
+			{
+				if (!do_tex_mat)
+				{
+					if (!do_xform)
 					{
-						tc.mV[1] = -tc.mV[1] ;
+						LLVector4a::memcpyNonAliased16((F32*) tex_coords.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32));
 					}
-					int_part = (S32)tc.mV[1] ;
-					if(int_part & 1) //odd number
+					else
 					{
-						tc.mV[1] = int_part + 1 - tc.mV[1] ;
+						for (S32 i = 0; i < num_vertices; i++)
+						{	
+							LLVector2 tc(vf.mTexCoords[i]);
+							xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+							*tex_coords++ = tc;	
+						}
 					}
-					else //even number
-					{
-						tc.mV[1] -= int_part ;
+				}
+				else
+				{ //do tex mat, no texgen, no atlas, no bump
+					for (S32 i = 0; i < num_vertices; i++)
+					{	
+						LLVector2 tc(vf.mTexCoords[i]);
+						//LLVector4a& norm = vf.mNormals[i];
+						//LLVector4a& center = *(vf.mCenter);
+
+						LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f);
+						tmp = tmp * *mTextureMatrix;
+						tc.mV[0] = tmp.mV[0];
+						tc.mV[1] = tmp.mV[1];
+						*tex_coords++ = tc;	
 					}
-					break;
-				case LLTexUnit::TAM_WRAP:
-					if(tc.mV[0] > 1.f)
-						tc.mV[0] -= (S32)(tc.mV[0] - 0.00001f) ;
-					else if(tc.mV[0] < -1.f)
-						tc.mV[0] -= (S32)(tc.mV[0] + 0.00001f) ;
-
-					if(tc.mV[1] > 1.f)
-						tc.mV[1] -= (S32)(tc.mV[1] - 0.00001f) ;
-					else if(tc.mV[1] < -1.f)
-						tc.mV[1] -= (S32)(tc.mV[1] + 0.00001f) ;
-
-					if(tc.mV[0] < 0.f)
-					{
-						tc.mV[0] = 1.0f + tc.mV[0] ;
+				}
+			}
+			else
+			{ //no bump, no atlas, tex gen planar
+				if (do_tex_mat)
+				{
+					for (S32 i = 0; i < num_vertices; i++)
+					{	
+						LLVector2 tc(vf.mTexCoords[i]);
+						LLVector4a& norm = vf.mNormals[i];
+						LLVector4a& center = *(vf.mCenter);
+						LLVector4a vec = vf.mPositions[i];	
+						vec.mul(scalea);
+						planarProjection(tc, norm, center, vec);
+						
+						LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f);
+						tmp = tmp * *mTextureMatrix;
+						tc.mV[0] = tmp.mV[0];
+						tc.mV[1] = tmp.mV[1];
+				
+						*tex_coords++ = tc;	
 					}
-					if(tc.mV[1] < 0.f)
-					{
-						tc.mV[1] = 1.0f + tc.mV[1] ;
+				}
+				else
+				{
+					for (S32 i = 0; i < num_vertices; i++)
+					{	
+						LLVector2 tc(vf.mTexCoords[i]);
+						LLVector4a& norm = vf.mNormals[i];
+						LLVector4a& center = *(vf.mCenter);
+						LLVector4a vec = vf.mPositions[i];	
+						vec.mul(scalea);
+						planarProjection(tc, norm, center, vec);
+						
+						xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+
+						*tex_coords++ = tc;	
 					}
-					break;
-				default:
-					break;
 				}
-			
-				tc.mV[0] = tcoord_xoffset + tcoord_xscale * tc.mV[0] ;
-				tc.mV[1] = tcoord_yoffset + tcoord_yscale * tc.mV[1] ;
 			}
+		}
+		else
+		{ //either bump mapped or in atlas, just do the whole expensive loop
+			for (S32 i = 0; i < num_vertices; i++)
+			{	
+				LLVector2 tc(vf.mTexCoords[i]);
 			
+				LLVector4a& norm = vf.mNormals[i];
+				
+				LLVector4a& center = *(vf.mCenter);
+		   
+				if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+				{
+					LLVector4a vec = vf.mPositions[i];
+				
+					vec.mul(scalea);
 
-			*tex_coords++ = tc;
-		
-			if (bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1))
-			{
-				LLVector3 tangent = vf.mVertices[i].mBinormal % vf.mVertices[i].mNormal;
+					switch (texgen)
+					{
+						case LLTextureEntry::TEX_GEN_PLANAR:
+							planarProjection(tc, norm, center, vec);
+							break;
+						case LLTextureEntry::TEX_GEN_SPHERICAL:
+							sphericalProjection(tc, norm, center, vec);
+							break;
+						case LLTextureEntry::TEX_GEN_CYLINDRICAL:
+							cylindricalProjection(tc, norm, center, vec);
+							break;
+						default:
+							break;
+					}		
+				}
 
-				LLMatrix3 tangent_to_object;
-				tangent_to_object.setRows(tangent, vf.mVertices[i].mBinormal, vf.mVertices[i].mNormal);
-				LLVector3 binormal = binormal_dir * tangent_to_object;
-				binormal = binormal * mat_normal;
-				
-				if (mDrawablep->isActive())
+				if (tex_mode && mTextureMatrix)
 				{
-					binormal *= bump_quat;
+					LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f);
+					tmp = tmp * *mTextureMatrix;
+					tc.mV[0] = tmp.mV[0];
+					tc.mV[1] = tmp.mV[1];
 				}
+				else
+				{
+					xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+				}
+
+				if(in_atlas)
+				{
+					//
+					//manually calculate tex-coord per vertex for varying address modes.
+					//should be removed if shader can handle this.
+					//
 
-				binormal.normVec();
-				tc += LLVector2( bump_s_primary_light_ray * tangent, bump_t_primary_light_ray * binormal );
+					S32 int_part = 0 ;
+					switch(mTexture->getAddressMode())
+					{
+					case LLTexUnit::TAM_CLAMP:
+						if(tc.mV[0] < 0.f)
+						{
+							tc.mV[0] = 0.f ;
+						}
+						else if(tc.mV[0] > 1.f)
+						{
+							tc.mV[0] = 1.f;
+						}
+
+						if(tc.mV[1] < 0.f)
+						{
+							tc.mV[1] = 0.f ;
+						}
+						else if(tc.mV[1] > 1.f)
+						{
+							tc.mV[1] = 1.f;
+						}
+						break;
+					case LLTexUnit::TAM_MIRROR:
+						if(tc.mV[0] < 0.f)
+						{
+							tc.mV[0] = -tc.mV[0] ;
+						}
+						int_part = (S32)tc.mV[0] ;
+						if(int_part & 1) //odd number
+						{
+							tc.mV[0] = int_part + 1 - tc.mV[0] ;
+						}
+						else //even number
+						{
+							tc.mV[0] -= int_part ;
+						}
+
+						if(tc.mV[1] < 0.f)
+						{
+							tc.mV[1] = -tc.mV[1] ;
+						}
+						int_part = (S32)tc.mV[1] ;
+						if(int_part & 1) //odd number
+						{
+							tc.mV[1] = int_part + 1 - tc.mV[1] ;
+						}
+						else //even number
+						{
+							tc.mV[1] -= int_part ;
+						}
+						break;
+					case LLTexUnit::TAM_WRAP:
+						if(tc.mV[0] > 1.f)
+							tc.mV[0] -= (S32)(tc.mV[0] - 0.00001f) ;
+						else if(tc.mV[0] < -1.f)
+							tc.mV[0] -= (S32)(tc.mV[0] + 0.00001f) ;
+
+						if(tc.mV[1] > 1.f)
+							tc.mV[1] -= (S32)(tc.mV[1] - 0.00001f) ;
+						else if(tc.mV[1] < -1.f)
+							tc.mV[1] -= (S32)(tc.mV[1] + 0.00001f) ;
+
+						if(tc.mV[0] < 0.f)
+						{
+							tc.mV[0] = 1.0f + tc.mV[0] ;
+						}
+						if(tc.mV[1] < 0.f)
+						{
+							tc.mV[1] = 1.0f + tc.mV[1] ;
+						}
+						break;
+					default:
+						break;
+					}
 				
-				*tex_coords2++ = tc;
-			}	
+					tc.mV[0] = tcoord_xoffset + tcoord_xscale * tc.mV[0] ;
+					tc.mV[1] = tcoord_yoffset + tcoord_yscale * tc.mV[1] ;
+				}
+				
+
+				*tex_coords++ = tc;
+				
+				if (bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1))
+				{
+					LLVector4a tangent;
+					tangent.setCross3(vf.mBinormals[i], vf.mNormals[i]);
+
+					LLMatrix4a tangent_to_object;
+					tangent_to_object.setRows(tangent, vf.mBinormals[i], vf.mNormals[i]);
+					LLVector4a t;
+					tangent_to_object.rotate(binormal_dir, t);
+					LLVector4a binormal;
+					mat_normal.rotate(t, binormal);
+						
+					//VECTORIZE THIS
+					if (mDrawablep->isActive())
+					{
+						LLVector3 t;
+						t.set(binormal.getF32ptr());
+						t *= bump_quat;
+						binormal.load3(t.mV);
+					}
+
+					binormal.normalize3fast();
+					tc += LLVector2( bump_s_primary_light_ray.dot3(tangent).getF32(), bump_t_primary_light_ray.dot3(binormal).getF32() );
+					
+					*tex_coords2++ = tc;
+				}	
+			}
 		}
-			
-		if (rebuild_pos)
-		{
-			*vertices++ = vf.mVertices[i].mPosition * mat_vert;
+	}
+
+	if (rebuild_pos)
+	{
+		LLMatrix4a mat_vert;
+		mat_vert.loadu(mat_vert_in);
+
+		LLVector4a* src = vf.mPositions;
+		LLVector4a* dst = vertices;
+
+		LLVector4a* end = dst+num_vertices;
+		do
+		{	
+			mat_vert.affineTransform(*src++, *dst++);
 		}
+		while(dst < end);
+	}
 		
-		if (rebuild_normal)
-		{
-			LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
-			normal.normVec();
-			
-			*normals++ = normal;
+	if (rebuild_normal)
+	{
+		for (S32 i = 0; i < num_vertices; i++)
+		{	
+			LLVector4a normal;
+			mat_normal.rotate(vf.mNormals[i], normal);
+			normal.normalize3fast();
+			normals[i] = normal;
 		}
+	}
 		
-		if (rebuild_binormal)
-		{
-			LLVector3 binormal = vf.mVertices[i].mBinormal * mat_normal;
-			binormal.normVec();
-			*binormals++ = binormal;
+	if (rebuild_binormal)
+	{
+		for (S32 i = 0; i < num_vertices; i++)
+		{	
+			LLVector4a binormal;
+			mat_normal.rotate(vf.mBinormals[i], binormal);
+			binormal.normalize3fast();
+			binormals[i] = binormal;
 		}
+	}
+	
+	if (rebuild_weights && vf.mWeights)
+	{
+		LLVector4a::memcpyNonAliased16((F32*) weights, (F32*) vf.mWeights, num_vertices*4*sizeof(F32));
+	}
+
+	if (rebuild_color)
+	{
+		LLVector4a src;
+
+		U32 vec[4];
+		vec[0] = vec[1] = vec[2] = vec[3] = color.mAll;
 		
-		if (rebuild_color)
+		src.loadua((F32*) vec);
+
+		LLVector4a* dst = (LLVector4a*) colors.get();
+		S32 num_vecs = num_vertices/4;
+		if (num_vertices%4 > 0)
 		{
-			*colors++ = color;		
+			++num_vecs;
+		}
+
+		for (S32 i = 0; i < num_vecs; i++)
+		{	
+			dst[i] = src;
 		}
 	}
 
@@ -1418,20 +1723,32 @@ F32 LLFace::getTextureVirtualSize()
 
 BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
 {
+	//VECTORIZE THIS
 	//get area of circle around face
-	LLVector3 center = getPositionAgent();
-	LLVector3 size = (mExtents[1] - mExtents[0]) * 0.5f;	
+	LLVector4a center;
+	center.load3(getPositionAgent().mV);
+	LLVector4a size;
+	size.setSub(mExtents[1], mExtents[0]);
+	size.mul(0.5f);
+
 	LLViewerCamera* camera = LLViewerCamera::getInstance();
 
-	F32 size_squared = size.lengthSquared() ;
-	LLVector3 lookAt = center - camera->getOrigin();
-	F32 dist = lookAt.normVec() ;	
+	F32 size_squared = size.dot3(size).getF32();
+	LLVector4a lookAt;
+	LLVector4a t;
+	t.load3(camera->getOrigin().mV);
+	lookAt.setSub(center, t);
+	F32 dist = lookAt.getLength3().getF32();
+	dist = llmax(dist-size.getLength3().getF32(), 0.f);
+	lookAt.normalize3fast() ;	
 
 	//get area of circle around node
-	F32 app_angle = atanf(fsqrtf(size_squared) / dist);
+	F32 app_angle = atanf((F32) sqrt(size_squared) / dist);
 	radius = app_angle*LLDrawable::sCurPixelAngle;
 	mPixelArea = radius*radius * 3.14159f;
-	cos_angle_to_view_dir = lookAt * camera->getXAxis() ;
+	LLVector4a x_axis;
+	x_axis.load3(camera->getXAxis().mV);
+	cos_angle_to_view_dir = lookAt.dot3(x_axis).getF32();
 
 	//if has media, check if the face is out of the view frustum.	
 	if(hasMedia())
@@ -1447,7 +1764,10 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
 		}
 		else
 		{		
-			if(dist * dist * (lookAt - camera->getXAxis()).lengthSquared() < size_squared)
+			LLVector4a d;
+			d.setSub(lookAt, x_axis);
+
+			if(dist * dist * d.dot3(d) < size_squared)
 			{
 				cos_angle_to_view_dir = 1.0f ;
 			}
@@ -1562,24 +1882,15 @@ BOOL LLFace::verify(const U32* indices_array) const
 	BOOL ok = TRUE;
 
 	if( mVertexBuffer.isNull() )
-	{
-		if( mGeomCount )
-		{
-			// This happens before teleports as faces are torn down.
-			// Stop the crash in DEV-31893 with a null pointer check,
-			// but present this info.
-			// To clean up the log, the geometry could be cleared, or the
-			// face could otherwise be marked for no ::verify.
-			llinfos << "Face with no vertex buffer and " << mGeomCount << " mGeomCount" << llendl;
-		}
+	{ //no vertex buffer, face is implicitly valid
 		return TRUE;
 	}
 	
 	// First, check whether the face data fits within the pool's range.
-	if ((mGeomIndex + mGeomCount) > mVertexBuffer->getNumVerts())
+	if ((mGeomIndex + mGeomCount) > mVertexBuffer->getRequestedVerts())
 	{
 		ok = FALSE;
-		llinfos << "Face not within pool range!" << llendl;
+		llinfos << "Face references invalid vertices!" << llendl;
 	}
 
 	S32 indices_count = (S32)getIndicesCount();
@@ -1595,6 +1906,12 @@ BOOL LLFace::verify(const U32* indices_array) const
 		llinfos << "Face has bogus indices count" << llendl;
 	}
 	
+	if (mIndicesIndex + mIndicesCount > mVertexBuffer->getRequestedIndices())
+	{
+		ok = FALSE;
+		llinfos << "Face references invalid indices!" << llendl;
+	}
+
 #if 0
 	S32 geom_start = getGeomStart();
 	S32 geom_count = mGeomCount;
@@ -1903,3 +2220,78 @@ BOOL LLFace::switchTexture()
 	return mUsingAtlas ;
 }
 
+
+void LLFace::setVertexBuffer(LLVertexBuffer* buffer)
+{
+	mVertexBuffer = buffer;
+	llassert(verify());
+}
+
+void LLFace::clearVertexBuffer()
+{
+	mVertexBuffer = NULL;
+	mLastVertexBuffer = NULL;
+}
+
+//static
+U32 LLFace::getRiggedDataMask(U32 type)
+{
+	static const U32 rigged_data_mask[] = {
+		LLDrawPoolAvatar::RIGGED_SIMPLE_MASK,
+		LLDrawPoolAvatar::RIGGED_FULLBRIGHT_MASK,
+		LLDrawPoolAvatar::RIGGED_SHINY_MASK,
+		LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY_MASK,
+		LLDrawPoolAvatar::RIGGED_GLOW_MASK,
+		LLDrawPoolAvatar::RIGGED_ALPHA_MASK,
+		LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA_MASK,
+		LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP_MASK,						 
+		LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE_MASK,
+	};
+
+	llassert(type < sizeof(rigged_data_mask)/sizeof(U32));
+
+	return rigged_data_mask[type];
+}
+
+U32 LLFace::getRiggedVertexBufferDataMask() const
+{
+	U32 data_mask = 0;
+	for (U32 i = 0; i < mRiggedIndex.size(); ++i)
+	{
+		if (mRiggedIndex[i] > -1)
+		{
+			data_mask |= LLFace::getRiggedDataMask(i);
+		}
+	}
+
+	return data_mask;
+}
+
+S32 LLFace::getRiggedIndex(U32 type) const
+{
+	if (mRiggedIndex.empty())
+	{
+		return -1;
+	}
+
+	llassert(type < mRiggedIndex.size());
+
+	return mRiggedIndex[type];
+}
+
+void LLFace::setRiggedIndex(U32 type, S32 index)
+{
+	if (mRiggedIndex.empty())
+	{
+		mRiggedIndex.resize(LLDrawPoolAvatar::NUM_RIGGED_PASSES);
+		for (U32 i = 0; i < mRiggedIndex.size(); ++i)
+		{
+			mRiggedIndex[i] = -1;
+		}
+	}
+
+	llassert(type < mRiggedIndex.size());
+
+	mRiggedIndex[type] = index;
+}
+
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 6c941bd092b75071c17ceac8e42367e89f164d00..b2170c4cf3fd271c7cfd63ab7006d752892c0c3b 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -59,6 +59,17 @@ class LLFace
 {
 public:
 
+	LLFace(const LLFace& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLFace& operator=(const LLFace& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
 	enum EMasks
 	{
 		LIGHT			= 0x0001,
@@ -67,6 +78,7 @@ class LLFace
 		HUD_RENDER		= 0x0008,
 		USE_FACE_COLOR	= 0x0010,
 		TEXTURE_ANIM	= 0x0020, 
+		RIGGED			= 0x0040,
 	};
 
 	static void initClass();
@@ -119,14 +131,14 @@ class LLFace
 	LLDrawable*		getDrawable()		const	{ return mDrawablep; }
 	LLViewerObject*	getViewerObject()	const	{ return mVObjp; }
 	S32				getLOD()			const	{ return mVObjp.notNull() ? mVObjp->getLOD() : 0; }
-	LLVertexBuffer* getVertexBuffer()	const	{ return mVertexBuffer; }
 	void			setPoolType(U32 type)		{ mPoolType = type; }
 	S32				getTEOffset()				{ return mTEOffset; }
 	LLViewerTexture*	getTexture() const;
 
 	void			setViewerObject(LLViewerObject* object);
 	void			setPool(LLFacePool *pool, LLViewerTexture *texturep);
-	
+	void			setPool(LLFacePool* pool);
+
 	void			setDrawable(LLDrawable *drawable);
 	void			setTEOffset(const S32 te_offset);
 	
@@ -142,7 +154,8 @@ class LLFace
 	BOOL getGeometryVolume(const LLVolume& volume,
 						const S32 &f,
 						const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
-						const U16 &index_offset);
+						const U16 &index_offset,
+						bool force_rebuild = false);
 
 	// For avatar
 	U16			 getGeometryAvatar(
@@ -161,7 +174,7 @@ class LLFace
 	S32 getColors(LLStrider<LLColor4U> &colors);
 	S32 getIndices(LLStrider<U16> &indices);
 
-	void		setSize(const S32 numVertices, const S32 num_indices = 0);
+	void		setSize(S32 numVertices, S32 num_indices = 0, bool align = false);
 	
 	BOOL		genVolumeBBoxes(const LLVolume &volume, S32 f,
 								   const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
@@ -183,8 +196,8 @@ class LLFace
 	BOOL		verify(const U32* indices_array = NULL) const;
 	void		printDebugInfo() const;
 
-	void		setGeomIndex(U16 idx) { mGeomIndex = idx; }
-	void		setIndicesIndex(S32 idx) { mIndicesIndex = idx; }
+	void		setGeomIndex(U16 idx); 
+	void		setIndicesIndex(S32 idx);
 	void		setDrawInfo(LLDrawInfo* draw_info);
 
 	F32         getTextureVirtualSize() ;
@@ -205,6 +218,19 @@ class LLFace
 	void                  removeAtlas() ;
 	BOOL                  switchTexture() ;
 
+	//vertex buffer tracking
+	void setVertexBuffer(LLVertexBuffer* buffer);
+	void clearVertexBuffer(); //sets mVertexBuffer and mLastVertexBuffer to NULL
+	LLVertexBuffer* getVertexBuffer()	const	{ return mVertexBuffer; }
+	U32 getRiggedVertexBufferDataMask() const;
+	S32 getRiggedIndex(U32 type) const;
+	void setRiggedIndex(U32 type, S32 index);
+
+	static U32 getRiggedDataMask(U32 type);
+
+public: //aligned members
+	LLVector4a		mExtents[2];
+
 private:	
 	F32         adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius );
 	BOOL        calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) ;
@@ -216,20 +242,19 @@ class LLFace
 	
 	LLVector3		mCenterLocal;
 	LLVector3		mCenterAgent;
-	LLVector3		mExtents[2];
+	
 	LLVector2		mTexExtents[2];
 	F32				mDistance;
-	LLPointer<LLVertexBuffer> mVertexBuffer;
-	LLPointer<LLVertexBuffer> mLastVertexBuffer;
 	F32			mLastUpdateTime;
+	F32			mLastSkinTime;
 	F32			mLastMoveTime;
 	LLMatrix4*	mTextureMatrix;
 	LLDrawInfo* mDrawInfo;
 
 private:
-	friend class LLGeometryManager;
-	friend class LLVolumeGeometryManager;
-
+	LLPointer<LLVertexBuffer> mVertexBuffer;
+	LLPointer<LLVertexBuffer> mLastVertexBuffer;
+	
 	U32			mState;
 	LLFacePool*	mDrawPoolp;
 	U32			mPoolType;
@@ -254,6 +279,8 @@ class LLFace
 	S32			mTEOffset;
 
 	S32			mReferenceIndex;
+	std::vector<S32> mRiggedIndex;
+	
 	F32			mVSize;
 	F32			mPixelArea;
 
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index 279904b74013201b5cf9ff88d81ee90aecccecfa..35712163ebd78ebf20f129671d046c9a6d1b7edd 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -32,7 +32,9 @@
 #include "llrect.h"
 #include "llerror.h"
 #include "llgl.h"
+#include "llimagepng.h"
 #include "llrender.h"
+#include "llrendertarget.h"
 #include "lllocalcliprect.h"
 #include "llmath.h"
 #include "llfontgl.h"
@@ -49,6 +51,8 @@
 #include "llfasttimer.h"
 #include "lltreeiterators.h"
 #include "llmetricperformancetester.h"
+#include "llviewerstats.h"
+
 //////////////////////////////////////////////////////////////////////////////
 
 static const S32 MAX_VISIBLE_HISTORY = 10;
@@ -1020,6 +1024,327 @@ F64 LLFastTimerView::getTime(const std::string& name)
 	return 0.0;
 }
 
+void saveChart(const std::string& label, const char* suffix, LLImageRaw* scratch)
+{
+	//read result back into raw image
+	glReadPixels(0, 0, 1024, 512, GL_RGB, GL_UNSIGNED_BYTE, scratch->getData());
+
+	//write results to disk
+	LLPointer<LLImagePNG> result = new LLImagePNG();
+	result->encode(scratch, 0.f);
+
+	std::string ext = result->getExtension();
+	std::string filename = llformat("%s_%s.%s", label.c_str(), suffix, ext.c_str());
+	
+	std::string out_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename);
+	result->save(out_file);
+}
+
+//static
+void LLFastTimerView::exportCharts(const std::string& base, const std::string& target)
+{
+	//allocate render target for drawing charts 
+	LLRenderTarget buffer;
+	buffer.allocate(1024,512, GL_RGB, FALSE, FALSE);
+	
+
+	LLSD cur;
+
+	LLSD base_data;
+
+	{ //read base log into memory
+		S32 i = 0;
+		std::ifstream is(base.c_str());
+		while (!is.eof() && LLSDSerialize::fromXML(cur, is))
+		{
+			base_data[i++] = cur;
+		}
+		is.close();
+	}
+
+	LLSD cur_data;
+	std::set<std::string> chart_names;
+
+	{ //read current log into memory
+		S32 i = 0;
+		std::ifstream is(target.c_str());
+		while (!is.eof() && LLSDSerialize::fromXML(cur, is))
+		{
+			cur_data[i++] = cur;
+
+			for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
+			{
+				std::string label = iter->first;
+				chart_names.insert(label);
+			}
+		}
+		is.close();
+	}
+
+	//get time domain
+	LLSD::Real cur_total_time = 0.0;
+
+	for (U32 i = 0; i < cur_data.size(); ++i)
+	{
+		cur_total_time += cur_data[i]["Total"]["Time"].asReal();
+	}
+
+	LLSD::Real base_total_time = 0.0;
+	for (U32 i = 0; i < base_data.size(); ++i)
+	{
+		base_total_time += base_data[i]["Total"]["Time"].asReal();
+	}
+
+	//allocate raw scratch space
+	LLPointer<LLImageRaw> scratch = new LLImageRaw(1024, 512, 3);
+
+	gGL.pushMatrix();
+	glLoadIdentity();
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(-0.05, 1.05, -0.05, 1.05, -1.0, 1.0);
+
+	//render charts
+	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+	
+	buffer.bindTarget();
+
+	for (std::set<std::string>::iterator iter = chart_names.begin(); iter != chart_names.end(); ++iter)
+	{
+		std::string label = *iter;
+	
+		LLSD::Real max_time = 0.0;
+		LLSD::Integer max_calls = 0;
+		LLSD::Real max_execution = 0.0;
+
+		std::vector<LLSD::Real> cur_execution;
+		std::vector<LLSD::Real> cur_times;
+		std::vector<LLSD::Integer> cur_calls;
+
+		std::vector<LLSD::Real> base_execution;
+		std::vector<LLSD::Real> base_times;
+		std::vector<LLSD::Integer> base_calls;
+
+		for (U32 i = 0; i < cur_data.size(); ++i)
+		{
+			LLSD::Real time = cur_data[i][label]["Time"].asReal();
+			LLSD::Integer calls = cur_data[i][label]["Calls"].asInteger();
+
+			LLSD::Real execution = 0.0;
+			if (calls > 0)
+			{
+				execution = time/calls;
+				cur_execution.push_back(execution);
+				cur_times.push_back(time);
+			}
+
+			cur_calls.push_back(calls);
+		}
+
+		for (U32 i = 0; i < base_data.size(); ++i)
+		{
+			LLSD::Real time = base_data[i][label]["Time"].asReal();
+			LLSD::Integer calls = base_data[i][label]["Calls"].asInteger();
+
+			LLSD::Real execution = 0.0;
+			if (calls > 0)
+			{
+				execution = time/calls;
+				base_execution.push_back(execution);
+				base_times.push_back(time);
+			}
+
+			base_calls.push_back(calls);
+		}
+
+		std::sort(base_calls.begin(), base_calls.end());
+		std::sort(base_times.begin(), base_times.end());
+		std::sort(base_execution.begin(), base_execution.end());
+
+		std::sort(cur_calls.begin(), cur_calls.end());
+		std::sort(cur_times.begin(), cur_times.end());
+		std::sort(cur_execution.begin(), cur_execution.end());
+
+		//remove outliers
+		const U32 OUTLIER_CUTOFF = 512;
+		if (base_times.size() > OUTLIER_CUTOFF)
+		{ 
+			ll_remove_outliers(base_times, 1.f);
+		}
+
+		if (base_execution.size() > OUTLIER_CUTOFF)
+		{ 
+			ll_remove_outliers(base_execution, 1.f);
+		}
+
+		if (cur_times.size() > OUTLIER_CUTOFF)
+		{ 
+			ll_remove_outliers(cur_times, 1.f);
+		}
+
+		if (cur_execution.size() > OUTLIER_CUTOFF)
+		{ 
+			ll_remove_outliers(cur_execution, 1.f);
+		}
+
+
+		max_time = llmax(base_times.empty() ? 0.0 : *base_times.rbegin(), cur_times.empty() ? 0.0 : *cur_times.rbegin());
+		max_calls = llmax(base_calls.empty() ? 0 : *base_calls.rbegin(), cur_calls.empty() ? 0 : *cur_calls.rbegin());
+		max_execution = llmax(base_execution.empty() ? 0.0 : *base_execution.rbegin(), cur_execution.empty() ? 0.0 : *cur_execution.rbegin());
+
+
+		LLVector3 last_p;
+
+		//====================================
+		// basic
+		//====================================
+		buffer.clear();
+
+		last_p.clear();
+
+		LLGLDisable cull(GL_CULL_FACE);
+
+		LLVector3 base_col(0, 0.7f, 0.f);
+		LLVector3 cur_col(1.f, 0.f, 0.f);
+
+		gGL.setSceneBlendType(LLRender::BT_ADD);
+
+		gGL.color3fv(base_col.mV);
+		for (U32 i = 0; i < base_times.size(); ++i)
+		{
+			gGL.begin(LLRender::TRIANGLE_STRIP);
+			gGL.vertex3fv(last_p.mV);
+			gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+			last_p.set((F32)i/(F32) base_times.size(), base_times[i]/max_time, 0.f);
+			gGL.vertex3fv(last_p.mV);
+			gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+			gGL.end();
+		}
+		
+		gGL.flush();
+
+		
+		last_p.clear();
+		{
+			LLGLEnable blend(GL_BLEND);
+						
+			gGL.color3fv(cur_col.mV);
+			for (U32 i = 0; i < cur_times.size(); ++i)
+			{
+				gGL.begin(LLRender::TRIANGLE_STRIP);
+				gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+				gGL.vertex3fv(last_p.mV);
+				last_p.set((F32) i / (F32) cur_times.size(), cur_times[i]/max_time, 0.f);
+				gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+				gGL.vertex3fv(last_p.mV);
+				gGL.end();
+			}
+			
+			gGL.flush();
+		}
+
+		saveChart(label, "time", scratch);
+		
+		//======================================
+		// calls
+		//======================================
+		buffer.clear();
+
+		last_p.clear();
+
+		gGL.color3fv(base_col.mV);
+		for (U32 i = 0; i < base_calls.size(); ++i)
+		{
+			gGL.begin(LLRender::TRIANGLE_STRIP);
+			gGL.vertex3fv(last_p.mV);
+			gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+			last_p.set((F32) i / (F32) base_calls.size(), (F32)base_calls[i]/max_calls, 0.f);
+			gGL.vertex3fv(last_p.mV);
+			gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+			gGL.end();
+		}
+		
+		gGL.flush();
+
+		{
+			LLGLEnable blend(GL_BLEND);
+			gGL.color3fv(cur_col.mV);
+			last_p.clear();
+
+			for (U32 i = 0; i < cur_calls.size(); ++i)
+			{
+				gGL.begin(LLRender::TRIANGLE_STRIP);
+				gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+				gGL.vertex3fv(last_p.mV);
+				last_p.set((F32) i / (F32) cur_calls.size(), (F32) cur_calls[i]/max_calls, 0.f);
+				gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+				gGL.vertex3fv(last_p.mV);
+				gGL.end();
+				
+			}
+			
+			gGL.flush();
+		}
+
+		saveChart(label, "calls", scratch);
+
+		//======================================
+		// execution
+		//======================================
+		buffer.clear();
+
+
+		gGL.color3fv(base_col.mV);
+		U32 count = 0;
+		U32 total_count = base_execution.size();
+
+		last_p.clear();
+
+		for (std::vector<LLSD::Real>::iterator iter = base_execution.begin(); iter != base_execution.end(); ++iter)
+		{
+			gGL.begin(LLRender::TRIANGLE_STRIP);
+			gGL.vertex3fv(last_p.mV);
+			gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+			last_p.set((F32)count/(F32)total_count, *iter/max_execution, 0.f);
+			gGL.vertex3fv(last_p.mV);
+			gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+			gGL.end();
+			count++;
+		}
+
+		last_p.clear();
+				
+		{
+			LLGLEnable blend(GL_BLEND);
+			gGL.color3fv(cur_col.mV);
+			count = 0;
+			total_count = cur_execution.size();
+
+			for (std::vector<LLSD::Real>::iterator iter = cur_execution.begin(); iter != cur_execution.end(); ++iter)
+			{
+				gGL.begin(LLRender::TRIANGLE_STRIP);
+				gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+				gGL.vertex3fv(last_p.mV);
+				last_p.set((F32)count/(F32)total_count, *iter/max_execution, 0.f);			
+				gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
+				gGL.vertex3fv(last_p.mV);
+				gGL.end();
+				count++;
+			}
+
+			gGL.flush();
+		}
+
+		saveChart(label, "execution", scratch);
+	}
+
+	buffer.flush();
+
+	gGL.popMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	gGL.popMatrix();
+}
+
 //static
 LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is)
 {
@@ -1030,6 +1355,10 @@ LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is)
 	LLSD::Real total_time = 0.0;
 	LLSD::Integer total_frames = 0;
 
+	typedef std::map<std::string,LLViewerStats::StatsAccumulator> stats_map_t;
+	stats_map_t time_stats;
+	stats_map_t sample_stats;
+
 	while (!is.eof() && LLSDSerialize::fromXML(cur, is))
 	{
 		for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
@@ -1046,35 +1375,31 @@ LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is)
 
 			if (time > 0.0)
 			{
-				ret[label]["TotalTime"] = ret[label]["TotalTime"].asReal() + time;
-				ret[label]["MaxTime"] = llmax(time, ret[label]["MaxTime"].asReal());
-
-				if (ret[label]["MinTime"].asReal() == 0)
-				{
-					ret[label]["MinTime"] = time;
-				}
-				else
-				{
-					ret[label]["MinTime"] = llmin(ret[label]["MinTime"].asReal(), time);
-				}
-				
 				LLSD::Integer samples = iter->second["Calls"].asInteger();
 
-				ret[label]["Samples"] = ret[label]["Samples"].asInteger() + samples;
-				ret[label]["MaxSamples"] = llmax(ret[label]["MaxSamples"].asInteger(), samples);
-
-				if (ret[label]["MinSamples"].asInteger() == 0)
-				{
-					ret[label]["MinSamples"] = samples;
-				}
-				else
-				{
-					ret[label]["MinSamples"] = llmin(ret[label]["MinSamples"].asInteger(), samples);
-				}
+				time_stats[label].push(time);
+				sample_stats[label].push(samples);
 			}
 		}
 		total_frames++;
 	}
+
+	for(stats_map_t::iterator it = time_stats.begin(); it != time_stats.end(); ++it)
+	{
+		std::string label = it->first;
+		ret[label]["TotalTime"] = time_stats[label].mSum;
+		ret[label]["MeanTime"] = time_stats[label].getMean();
+		ret[label]["MaxTime"] = time_stats[label].getMaxValue();
+		ret[label]["MinTime"] = time_stats[label].getMinValue();
+		ret[label]["StdDevTime"] = time_stats[label].getStdDev();
+		
+		ret[label]["Samples"] = sample_stats[label].mSum;
+		ret[label]["MaxSamples"] = sample_stats[label].getMaxValue();
+		ret[label]["MinSamples"] = sample_stats[label].getMinValue();
+		ret[label]["StdDevSamples"] = sample_stats[label].getStdDev();
+
+		ret[label]["Frames"] = (LLSD::Integer)time_stats[label].getCount();
+	}
 		
 	ret["SessionTime"] = total_time;
 	ret["FrameCount"] = total_frames;
@@ -1109,8 +1434,27 @@ void LLFastTimerView::doAnalysisDefault(std::string baseline, std::string target
 	std::ofstream os(output.c_str());
 
 	LLSD::Real session_time = current["SessionTime"].asReal();
-	
-	os << "Label, % Change, % of Session, Cur Min, Cur Max, Cur Mean, Cur Total, Cur Samples, Base Min, Base Max, Base Mean, Base Total, Base Samples\n"; 
+	os <<
+		"Label, "
+		"% Change, "
+		"% of Session, "
+		"Cur Min, "
+		"Cur Max, "
+		"Cur Mean/sample, "
+		"Cur Mean/frame, "
+		"Cur StdDev/frame, "
+		"Cur Total, "
+		"Cur Frames, "
+		"Cur Samples, "
+		"Base Min, "
+		"Base Max, "
+		"Base Mean/sample, "
+		"Base Mean/frame, "
+		"Base StdDev/frame, "
+		"Base Total, "
+		"Base Frames, "
+		"Base Samples\n"; 
+
 	for (LLSD::map_iterator iter = base.beginMap();  iter != base.endMap(); ++iter)
 	{
 		LLSD::String label = iter->first;
@@ -1122,29 +1466,37 @@ void LLFastTimerView::doAnalysisDefault(std::string baseline, std::string target
 			continue;
 		}	
 		LLSD::Real a = base[label]["TotalTime"].asReal() / base[label]["Samples"].asReal();
-		LLSD::Real b = current[label]["TotalTime"].asReal() / base[label]["Samples"].asReal();
+		LLSD::Real b = current[label]["TotalTime"].asReal() / current[label]["Samples"].asReal();
 			
 		LLSD::Real diff = b-a;
 
 		LLSD::Real perc = diff/a * 100;
 
-		os << llformat("%s, %.2f, %.4f, %.4f, %.4f, %.4f, %.4f, %d, %.4f, %.4f, %.4f, %.4f, %d\n",
+		os << llformat("%s, %.2f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %d, %d, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %d, %d\n",
 			label.c_str(), 
 			(F32) perc, 
 			(F32) (current[label]["TotalTime"].asReal()/session_time * 100.0), 
+
 			(F32) current[label]["MinTime"].asReal(), 
 			(F32) current[label]["MaxTime"].asReal(), 
 			(F32) b, 
+			(F32) current[label]["MeanTime"].asReal(), 
+			(F32) current[label]["StdDevTime"].asReal(),
 			(F32) current[label]["TotalTime"].asReal(), 
+			current[label]["Frames"].asInteger(),
 			current[label]["Samples"].asInteger(),
 			(F32) base[label]["MinTime"].asReal(), 
 			(F32) base[label]["MaxTime"].asReal(), 
 			(F32) a, 
+			(F32) base[label]["MeanTime"].asReal(), 
+			(F32) base[label]["StdDevTime"].asReal(),
 			(F32) base[label]["TotalTime"].asReal(), 
+			base[label]["Frames"].asInteger(),
 			base[label]["Samples"].asInteger());			
 	}
 
-	
+	exportCharts(baseline, target);
+
 	os.flush();
 	os.close();
 }
diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h
index b40d7ffc1a20642b39636c6936565fe3a71fa702..ea8251191b29e6a4fd50ca3d16d505aa1364a8e9 100644
--- a/indra/newview/llfasttimerview.h
+++ b/indra/newview/llfasttimerview.h
@@ -43,6 +43,7 @@ class LLFastTimerView : public LLFloater
 private:
 	static void doAnalysisDefault(std::string baseline, std::string target, std::string output) ;
 	static LLSD analyzePerformanceLogDefault(std::istream& is) ;
+	static void exportCharts(const std::string& base, const std::string& target);
 
 public:
 
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index 4e16cc4217dcb1c1c67f7fa944c929b2f8a822e2..524d2d74ef3aae894ea735cf57a6a3a41b6159d9 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -729,6 +729,10 @@ void LLFeatureManager::applyBaseMasks()
 	{
 		maskFeatures("ATI");
 	}
+	if (gGLManager.mHasATIMemInfo && gGLManager.mVRAM < 256)
+	{
+		maskFeatures("ATIVramLT256");
+	}
 	if (gGLManager.mATIOldDriver)
 	{
 		maskFeatures("ATIOldDriver");
@@ -745,6 +749,14 @@ void LLFeatureManager::applyBaseMasks()
 	{
 		maskFeatures("OpenGLPre15");
 	}
+	if (gGLManager.mGLVersion < 3.f)
+	{
+		maskFeatures("OpenGLPre30");
+	}
+	if (gGLManager.mNumTextureUnits <= 8)
+	{
+		maskFeatures("TexUnit8orLess");
+	}
 
 	// now mask by gpu string
 	// Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 51e76bcf9bd2216187b843790502deac7c24e251..8c0ed298551cdb32294b472fd98a668a21516930 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -50,12 +50,14 @@ LLFilePicker LLFilePicker::sInstance;
 #define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0"
 #define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
 #define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0"
+#define COLLADA_FILTER L"Scene (*.dae)\0*.dae\0"
 #ifdef _CORY_TESTING
 #define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0"
 #endif
 #define XML_FILTER L"XML files (*.xml)\0*.xml\0"
 #define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
 #define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
+#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
 #endif
 
 //
@@ -185,6 +187,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
 		mOFN.lpstrFilter = ANIM_FILTER \
 			L"\0";
 		break;
+	case FFLOAD_COLLADA:
+		mOFN.lpstrFilter = COLLADA_FILTER \
+			L"\0";
+		break;
 #ifdef _CORY_TESTING
 	case FFLOAD_GEOMETRY:
 		mOFN.lpstrFilter = GEOMETRY_FILTER \
@@ -203,6 +209,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
 		mOFN.lpstrFilter = RAW_FILTER \
 			L"\0";
 		break;
+	case FFLOAD_MODEL:
+		mOFN.lpstrFilter = MODEL_FILTER \
+			L"\0";
+		break;
 	default:
 		res = FALSE;
 		break;
@@ -210,7 +220,7 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
 	return res;
 }
 
-BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
+BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
 {
 	if( mLocked )
 	{
@@ -235,8 +245,11 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
 
 	setupFilter(filter);
 	
-	// Modal, so pause agent
-	send_agent_pause();
+	if (blocking)
+	{
+		// Modal, so pause agent
+		send_agent_pause();
+	}
 
 	reset();
 	
@@ -247,10 +260,14 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
 		std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
 		mFiles.push_back(filename);
 	}
-	send_agent_resume();
 
-	// Account for the fact that the app has been stalled.
-	LLFrameTimer::updateFrameTime();
+	if (blocking)
+	{
+		send_agent_resume();
+		// Account for the fact that the app has been stalled.
+		LLFrameTimer::updateFrameTime();
+	}
+	
 	return success;
 }
 
@@ -570,6 +587,15 @@ Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callB
 								result = false;
 							}
 						}
+						else if (filter == FFLOAD_COLLADA)
+						{
+							if (fileInfo.filetype != 'DAE ' && 
+								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("dae"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+							)
+							{
+								result = false;
+							}
+						}
 #ifdef _CORY_TESTING
 						else if (filter == FFLOAD_GEOMETRY)
 						{
@@ -841,7 +867,7 @@ OSStatus	LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& fi
 	return error;
 }
 
-BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
+BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
 {
 	if( mLocked )
 		return FALSE;
@@ -866,20 +892,29 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
 		mNavOptions.optionFlags |= kNavSupportPackages;
 	}
 	
-	// Modal, so pause agent
-	send_agent_pause();
+	if (blocking)
+	{
+		// Modal, so pause agent
+		send_agent_pause();
+	}
+
 	{
 		error = doNavChooseDialog(filter);
 	}
-	send_agent_resume();
+	
 	if (error == noErr)
 	{
 		if (getFileCount())
 			success = true;
 	}
 
-	// Account for the fact that the app has been stalled.
-	LLFrameTimer::updateFrameTime();
+	if (blocking)
+	{
+		send_agent_resume();
+		// Account for the fact that the app has been stalled.
+		LLFrameTimer::updateFrameTime();
+	}
+
 	return success;
 }
 
@@ -1140,6 +1175,12 @@ static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker)
 						       LLTrans::getString("animation_files") + " (*.bvh)");
 }
 
+static std::string add_collada_filter_to_gtkchooser(GtkWindow *picker)
+{
+	return add_simple_pattern_filter_to_gtkchooser(picker,  "*.dae",
+						       LLTrans::getString("scene_files") + " (*.dae)");
+}
+
 static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker)
 {
 	GtkFileFilter *gfilter = gtk_file_filter_new();
@@ -1248,7 +1289,7 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename
 	return rtn;
 }
 
-BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking )
 {
 	BOOL rtn = FALSE;
 
@@ -1276,6 +1317,9 @@ BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
 		case FFLOAD_ANIM:
 			filtername = add_bvh_filter_to_gtkchooser(picker);
 			break;
+		case FFLOAD_COLLADA:
+			filtername = add_collada_filter_to_gtkchooser(picker);
+			break;
 		case FFLOAD_IMAGE:
 			filtername = add_imageload_filter_to_gtkchooser(picker);
 			break;
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 596bfa3e6953859bebdeef9748d8bb6dd30c95d7..cd843a8f33f322e5cf3a50f44c4eea49fdf22f79 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -82,6 +82,8 @@ class LLFilePicker
 		FFLOAD_XML = 6,
 		FFLOAD_SLOBJECT = 7,
 		FFLOAD_RAW = 8,
+		FFLOAD_MODEL = 9,
+		FFLOAD_COLLADA = 10,
 	};
 
 	enum ESaveFilter
@@ -105,7 +107,7 @@ class LLFilePicker
 
 	// open the dialog. This is a modal operation
 	BOOL getSaveFile( ESaveFilter filter = FFSAVE_ALL, const std::string& filename = LLStringUtil::null );
-	BOOL getOpenFile( ELoadFilter filter = FFLOAD_ALL );
+	BOOL getOpenFile( ELoadFilter filter = FFLOAD_ALL, bool blocking = true  );
 	BOOL getMultipleOpenFiles( ELoadFilter filter = FFLOAD_ALL );
 
 	// Get the filename(s) found. getFirstFile() sets the pointer to
@@ -190,4 +192,6 @@ class LLFilePicker
 	~LLFilePicker();
 };
 
+const std::string upload_pick(void* data);
+
 #endif
diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp
index 8ab2229235b4a8cd41929fafd0cf410db2a73e43..3d1650d2f527fe3432f0fe6e824b7401ff445991 100644
--- a/indra/newview/llflexibleobject.cpp
+++ b/indra/newview/llflexibleobject.cpp
@@ -91,11 +91,13 @@ void LLVolumeImplFlexible::onParameterChanged(U16 param_type, LLNetworkData *dat
 	}
 }
 
-void LLVolumeImplFlexible::onShift(const LLVector3 &shift_vector)
+void LLVolumeImplFlexible::onShift(const LLVector4a &shift_vector)
 {	
+	//VECTORIZE THIS
+	LLVector3 shift(shift_vector.getF32ptr());
 	for (int section = 0; section < (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1; ++section)
 	{
-		mSection[section].mPosition += shift_vector;	
+		mSection[section].mPosition += shift;	
 	}
 }
 
@@ -314,11 +316,13 @@ BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6
 		return FALSE; // (we are not initialized or updated)
 	}
 
-	if (force_update)
+	bool visible = mVO->mDrawable->isVisible();
+
+	if (force_update && visible)
 	{
 		gPipeline.markRebuild(mVO->mDrawable, LLDrawable::REBUILD_POSITION, FALSE);
 	}
-	else if	(mVO->mDrawable->isVisible() &&
+	else if	(visible &&
 		!mVO->mDrawable->isState(LLDrawable::IN_REBUILD_Q1) &&
 		mVO->getPixelArea() > 256.f)
 	{
@@ -362,7 +366,7 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
 	LLFastTimer ftm(FTM_DO_FLEXIBLE_UPDATE);
 	LLVolume* volume = mVO->getVolume();
 	LLPath *path = &volume->getPath();
-	if (mSimulateRes == 0)
+	if ((mSimulateRes == 0 || !mInitialized) && mVO->mDrawable->isVisible()) // if its uninitialized but not visible, what then? - Nyx
 	{
 		mVO->markForUpdate(TRUE);
 		if (!doIdleUpdate(gAgent, *LLWorld::getInstance(), 0.0))
@@ -690,6 +694,8 @@ BOOL LLVolumeImplFlexible::doUpdateGeometry(LLDrawable *drawable)
 	}
 
 	volume->updateRelativeXform();
+
+	if (mRenderRes > -1)
 	{
 		LLFastTimer t(FTM_DO_FLEXIBLE_UPDATE);
 		doFlexibleUpdate();
diff --git a/indra/newview/llflexibleobject.h b/indra/newview/llflexibleobject.h
index 9b952f1985f3c07037ce782527de570f7ce59b86..fef43d464d2dccb9dbffe439effe1246a0e8f514 100644
--- a/indra/newview/llflexibleobject.h
+++ b/indra/newview/llflexibleobject.h
@@ -84,7 +84,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface
 		void onSetVolume(const LLVolumeParams &volume_params, const S32 detail);
 		void onSetScale(const LLVector3 &scale, BOOL damped);
 		void onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin);
-		void onShift(const LLVector3 &shift_vector);
+		void onShift(const LLVector4a &shift_vector);
 		bool isVolumeUnique() const { return true; }
 		bool isVolumeGlobal() const { return true; }
 		bool isActive() const { return true; }
diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp
index deebd69ec102be55808eb047daf07f6481bb7c2f..1f334815d6a1e42c56232e051d6f6169cf4758a2 100644
--- a/indra/newview/llfloateranimpreview.cpp
+++ b/indra/newview/llfloateranimpreview.cpp
@@ -994,6 +994,7 @@ void LLFloaterAnimPreview::onBtnOK(void* userdata)
 						    LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(),
 						    name,
 						    callback, expected_upload_cost, userdata);
+
 			}
 			else
 			{
@@ -1032,7 +1033,6 @@ LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicT
 	mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
 	mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET);
 	mDummyAvatar->hideSkirt();
-	gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance());
 
 	// stop extraneous animations
 	mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE );
diff --git a/indra/newview/llfloaterhardwaresettings.cpp b/indra/newview/llfloaterhardwaresettings.cpp
index 1e9171055224f6f2bdffed89e10d0cf47032da2a..42ec7d765b4a6313b5fc69abbdbd4a818327394c 100644
--- a/indra/newview/llfloaterhardwaresettings.cpp
+++ b/indra/newview/llfloaterhardwaresettings.cpp
@@ -50,7 +50,6 @@ LLFloaterHardwareSettings::LLFloaterHardwareSettings(const LLSD& key)
 	  // but init them anyway
 	  mUseVBO(0),
 	  mUseAniso(0),
-	  mUseFBO(0),
 	  mFSAASamples(0),
 	  mGamma(0.0),
 	  mVideoCardMem(0),
@@ -75,7 +74,6 @@ void LLFloaterHardwareSettings::refresh()
 
 	mUseVBO = gSavedSettings.getBOOL("RenderVBOEnable");
 	mUseAniso = gSavedSettings.getBOOL("RenderAnisotropic");
-	mUseFBO = gSavedSettings.getBOOL("RenderUseFBO");
 	mFSAASamples = gSavedSettings.getU32("RenderFSAASamples");
 	mGamma = gSavedSettings.getF32("RenderGamma");
 	mVideoCardMem = gSavedSettings.getS32("TextureMemory");
@@ -104,7 +102,7 @@ void LLFloaterHardwareSettings::refreshEnabledState()
 	getChildView("(brightness, lower is brighter)")->setEnabled(!gPipeline.canUseWindLightShaders());
 	getChildView("fog")->setEnabled(!gPipeline.canUseWindLightShaders());
 	getChildView("fsaa")->setEnabled(gPipeline.canUseAntiAliasing());
-	getChildView("antialiasing restart")->setVisible(!gSavedSettings.getBOOL("RenderUseFBO"));
+	getChildView("antialiasing restart")->setVisible(!gSavedSettings.getBOOL("RenderDeferred"));
 
 	/* Enable to reset fsaa value to disabled when feature is not available.
 	if (!gPipeline.canUseAntiAliasing())
@@ -139,7 +137,6 @@ void LLFloaterHardwareSettings::cancel()
 {
 	gSavedSettings.setBOOL("RenderVBOEnable", mUseVBO);
 	gSavedSettings.setBOOL("RenderAnisotropic", mUseAniso);
-	gSavedSettings.setBOOL("RenderUseFBO", mUseFBO);
 	gSavedSettings.setU32("RenderFSAASamples", mFSAASamples);
 	gSavedSettings.setF32("RenderGamma", mGamma);
 	gSavedSettings.setS32("TextureMemory", mVideoCardMem);
diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp
index d76e7885bcac273a836836fe434db460c721afe2..e4d8e3513d72f79ed481a5a5f27703da8c5e0128 100644
--- a/indra/newview/llfloaterimagepreview.cpp
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -63,7 +63,7 @@ const S32 PREVIEW_BORDER_WIDTH = 2;
 const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
 const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
 const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16;
-const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+const S32 PREVIEW_TEXTURE_HEIGHT = 320;
 
 //-----------------------------------------------------------------------------
 // LLFloaterImagePreview()
@@ -787,8 +787,8 @@ void LLImagePreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance)
 	}
 
 	const LLVolumeFace &vf = mVolume->getVolumeFace(0);
-	U32 num_indices = vf.mIndices.size();
-	U32 num_vertices = vf.mVertices.size();
+	U32 num_indices = vf.mNumIndices;
+	U32 num_vertices = vf.mNumVertices;
 
 	mVertexBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL, 0);
 	mVertexBuffer->allocateBuffer(num_vertices, num_indices, TRUE);
@@ -802,10 +802,16 @@ void LLImagePreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance)
 	mVertexBuffer->getIndexStrider(index_strider);
 
 	// build vertices and normals
+	LLStrider<LLVector3> pos;
+	pos = (LLVector3*) vf.mPositions; pos.setStride(16);
+	LLStrider<LLVector3> norm;
+	norm = (LLVector3*) vf.mNormals; norm.setStride(16);
+		
+
 	for (U32 i = 0; i < num_vertices; i++)
 	{
-		*(vertex_strider++) = vf.mVertices[i].mPosition;
-		LLVector3 normal = vf.mVertices[i].mNormal;
+		*(vertex_strider++) = *pos++;
+		LLVector3 normal = *norm++;
 		normal.normalize();
 		*(normal_strider++) = normal;
 	}
@@ -824,7 +830,6 @@ void LLImagePreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance)
 BOOL LLImagePreviewSculpted::render()
 {
 	mNeedsUpdate = FALSE;
-
 	LLGLSUIDefault def;
 	LLGLDisable no_blend(GL_BLEND);
 	LLGLEnable cull(GL_CULL_FACE);
@@ -869,7 +874,7 @@ BOOL LLImagePreviewSculpted::render()
 	LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE);
 
 	const LLVolumeFace &vf = mVolume->getVolumeFace(0);
-	U32 num_indices = vf.mIndices.size();
+	U32 num_indices = vf.mNumIndices;
 	
 	mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL);
 
@@ -882,7 +887,6 @@ BOOL LLImagePreviewSculpted::render()
 	mVertexBuffer->draw(LLRender::TRIANGLES, num_indices, 0);
 
 	gGL.popMatrix();
-		
 	return TRUE;
 }
 
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8da1aa42cdde4edfdcbd4b44f1d33d0672ab11f
--- /dev/null
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -0,0 +1,5017 @@
+/**
+ * @file llfloatermodelpreview.cpp
+ * @brief LLFloaterModelPreview class implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "dae.h"
+//#include "dom.h"
+#include "dom/domAsset.h"
+#include "dom/domBind_material.h"
+#include "dom/domCOLLADA.h"
+#include "dom/domConstants.h"
+#include "dom/domController.h"
+#include "dom/domEffect.h"
+#include "dom/domGeometry.h"
+#include "dom/domInstance_geometry.h"
+#include "dom/domInstance_material.h"
+#include "dom/domInstance_node.h"
+#include "dom/domInstance_effect.h"
+#include "dom/domMaterial.h"
+#include "dom/domMatrix.h"
+#include "dom/domNode.h"
+#include "dom/domProfile_COMMON.h"
+#include "dom/domRotate.h"
+#include "dom/domScale.h"
+#include "dom/domTranslate.h"
+#include "dom/domVisual_scene.h"
+
+#include "llfloatermodelpreview.h"
+
+#include "llfilepicker.h"
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "lldatapacker.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llrender.h"
+#include "llface.h"
+#include "lleconomy.h"
+#include "llfocusmgr.h"
+#include "llfloaterperms.h"
+#include "lliconctrl.h"
+#include "llmatrix4a.h"
+#include "llmenubutton.h"
+#include "llmeshrepository.h"
+#include "llsdutil_math.h"
+#include "lltextbox.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llvector4a.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoavatarself.h"
+#include "pipeline.h"
+#include "lluictrlfactory.h"
+#include "llviewercontrol.h"
+#include "llviewermenu.h"
+#include "llviewermenufile.h"
+#include "llviewerregion.h"
+#include "llviewertexturelist.h"
+#include "llstring.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
+#include "llsdserialize.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltoggleablemenu.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "llcallbacklist.h"
+
+#include "glod/glod.h"
+
+//static
+S32 LLFloaterModelPreview::sUploadAmount = 10;
+LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
+std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
+
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16;
+const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+
+void drawBoxOutline(const LLVector3& pos, const LLVector3& size);
+
+
+std::string lod_name[NUM_LOD+1] =
+{
+	"lowest",
+	"low",
+	"medium",
+	"high",
+	"I went off the end of the lod_name array.  Me so smart."
+};
+
+std::string lod_triangles_name[NUM_LOD+1] =
+{
+	"lowest_triangles",
+	"low_triangles",
+	"medium_triangles",
+	"high_triangles",
+	"I went off the end of the lod_triangles_name array.  Me so smart."
+};
+
+std::string lod_vertices_name[NUM_LOD+1] =
+{
+	"lowest_vertices",
+	"low_vertices",
+	"medium_vertices",
+	"high_vertices",
+	"I went off the end of the lod_vertices_name array.  Me so smart."
+};
+
+std::string lod_status_name[NUM_LOD+1] =
+{
+	"lowest_status",
+	"low_status",
+	"medium_status",
+	"high_status",
+	"I went off the end of the lod_status_name array.  Me so smart."
+};
+
+std::string lod_icon_name[NUM_LOD+1] =
+{
+	"status_icon_lowest",
+	"status_icon_low",
+	"status_icon_medium",
+	"status_icon_high",
+	"I went off the end of the lod_status_name array.  Me so smart."
+};
+
+std::string lod_status_image[NUM_LOD+1] =
+{
+	"ModelImport_Status_Good",
+	"ModelImport_Status_Warning",
+	"ModelImport_Status_Error",
+	"I went off the end of the lod_status_image array.  Me so smart."
+};
+
+std::string lod_label_name[NUM_LOD+1] =
+{
+	"lowest_label",
+	"low_label",
+	"medium_label",
+	"high_label",
+	"I went off the end of the lod_label_name array.  Me so smart."
+};
+
+bool validate_face(const LLVolumeFace& face)
+{
+	for (U32 i = 0; i < face.mNumIndices; ++i)
+	{
+		if (face.mIndices[i] >= face.mNumVertices)
+		{
+			llwarns << "Face has invalid index." << llendl;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool validate_model(const LLModel* mdl)
+{
+	if (mdl->getNumVolumeFaces() == 0)
+	{
+		llwarns << "Model has no faces!" << llendl;
+		return false;
+	}
+
+	for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+	{
+		if (mdl->getVolumeFace(i).mNumVertices == 0)
+		{
+			llwarns << "Face has no vertices." << llendl;
+			return false;
+		}
+
+		if (mdl->getVolumeFace(i).mNumIndices == 0)
+		{
+			llwarns << "Face has no indices." << llendl;
+			return false;
+		}
+
+		if (!validate_face(mdl->getVolumeFace(i)))
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+BOOL stop_gloderror()
+{
+	GLuint error = glodGetError();
+
+	if (error != GLOD_NO_ERROR)
+	{
+		llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod)
+	: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
+	{
+		mMP = mp;
+		mLOD = lod;
+	}
+
+void LLMeshFilePicker::notify(const std::string& filename)
+{
+	mMP->loadModel(mFile, mLOD);
+}
+
+
+//-----------------------------------------------------------------------------
+// LLFloaterModelPreview()
+//-----------------------------------------------------------------------------
+LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) :
+LLFloater(key)
+{
+	sInstance = this;
+	mLastMouseX = 0;
+	mLastMouseY = 0;
+	mGLName = 0;
+	mStatusLock = new LLMutex(NULL);
+
+	mLODMode[LLModel::LOD_HIGH] = 0;
+	for (U32 i = 0; i < LLModel::LOD_HIGH; i++)
+	{
+		mLODMode[i] = 1;
+	}
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::postBuild()
+{
+	if (!LLFloater::postBuild())
+	{
+		return FALSE;
+	}
+
+	childSetAction("lod_browse", onBrowseLOD, this);
+
+	childSetCommitCallback("cancel_btn", onCancel, this);
+	childSetCommitCallback("crease_angle", onGenerateNormalsCommit, this);
+	childSetCommitCallback("generate_normals", onGenerateNormalsCommit, this);
+
+	childSetCommitCallback("lod_generate", onAutoFillCommit, this);
+
+	childSetCommitCallback("lod_mode", onLODParamCommit, this);
+	childSetCommitCallback("lod_error_threshold", onLODParamCommit, this);
+	childSetCommitCallback("lod_triangle_limit", onLODParamCommitTriangleLimit, this);
+	childSetCommitCallback("build_operator", onLODParamCommit, this);
+	childSetCommitCallback("queue_mode", onLODParamCommit, this);
+	childSetCommitCallback("border_mode", onLODParamCommit, this);
+	childSetCommitCallback("share_tolerance", onLODParamCommit, this);
+
+	childSetTextArg("status", "[STATUS]", getString("status_idle"));
+
+	//childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount));
+	childSetAction("ok_btn", onUpload, this);
+	childDisable("ok_btn");
+
+	childSetAction("reset_btn", onReset, this);
+
+	childSetAction("clear_materials", onClearMaterials, this);
+
+	childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this);
+
+	childSetCommitCallback("upload_skin", onUploadSkinCommit, this);
+	childSetCommitCallback("upload_joints", onUploadJointsCommit, this);
+
+	childSetCommitCallback("import_scale", onImportScaleCommit, this);
+	childSetCommitCallback("pelvis_offset", onPelvisOffsetCommit, this);
+
+	childSetCommitCallback("lod_file_or_limit", refresh, this);
+	childSetCommitCallback("physics_load_radio", refresh, this);
+	//childSetCommitCallback("physics_optimize", refresh, this);
+	//childSetCommitCallback("physics_use_hull", refresh, this);
+
+	childDisable("upload_skin");
+	childDisable("upload_joints");
+
+	childDisable("ok_btn");
+
+	mViewOptionMenuButton = getChild<LLMenuButton>("options_gear_btn");
+
+	mCommitCallbackRegistrar.add("ModelImport.ViewOption.Action", boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _2));
+	mEnableCallbackRegistrar.add("ModelImport.ViewOption.Check", boost::bind(&LLFloaterModelPreview::isViewOptionChecked, this, _2));
+	mEnableCallbackRegistrar.add("ModelImport.ViewOption.Enabled", boost::bind(&LLFloaterModelPreview::isViewOptionEnabled, this, _2));
+
+
+
+	mViewOptionMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_model_import_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	mViewOptionMenuButton->setMenu(mViewOptionMenu, LLMenuButton::MP_BOTTOM_LEFT);
+
+	initDecompControls();
+
+	LLView* preview_panel = getChild<LLView>("preview_panel");
+
+	mPreviewRect = preview_panel->getRect();
+
+	mModelPreview = new LLModelPreview(512, 512, this );
+	mModelPreview->setPreviewTarget(16.f);
+	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5));
+
+	//set callbacks for left click on line editor rows
+	for (U32 i = 0; i <= LLModel::LOD_HIGH; i++)
+	{
+		LLTextBox* text = getChild<LLTextBox>(lod_label_name[i]);
+		if (text)
+		{
+			text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i));
+		}
+
+		text = getChild<LLTextBox>(lod_triangles_name[i]);
+		if (text)
+		{
+			text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i));
+		}
+
+		text = getChild<LLTextBox>(lod_vertices_name[i]);
+		if (text)
+		{
+			text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i));
+		}
+
+		text = getChild<LLTextBox>(lod_status_name[i]);
+		if (text)
+		{
+			text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i));
+		}
+	}
+
+	return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterModelPreview()
+//-----------------------------------------------------------------------------
+LLFloaterModelPreview::~LLFloaterModelPreview()
+{
+	sInstance = NULL;
+	
+	if ( mModelPreview && mModelPreview->getResetJointFlag() )
+	{		
+		gAgentAvatarp->resetJointPositions();
+	}
+
+	
+	if ( mModelPreview )
+	{
+		delete mModelPreview;
+	}
+
+	if (mGLName)
+	{
+		LLImageGL::deleteTextures(1, &mGLName );
+	}
+
+	delete mStatusLock;
+	mStatusLock = NULL;
+}
+
+void LLFloaterModelPreview::onViewOptionChecked(const LLSD& userdata)
+{
+	if (mModelPreview)
+	{
+		mModelPreview->mViewOption[userdata.asString()] = !mModelPreview->mViewOption[userdata.asString()];
+		
+		mModelPreview->refresh();
+	}
+}
+
+bool LLFloaterModelPreview::isViewOptionChecked(const LLSD& userdata)
+{
+	if (mModelPreview)
+	{
+		return mModelPreview->mViewOption[userdata.asString()];
+	}
+
+	return false;
+}
+
+bool LLFloaterModelPreview::isViewOptionEnabled(const LLSD& userdata)
+{
+	return !mViewOptionDisabled[userdata.asString()];
+}
+
+void LLFloaterModelPreview::setViewOptionEnabled(const std::string& option, bool enabled)
+{
+	mViewOptionDisabled[option] = !enabled;
+}
+
+void LLFloaterModelPreview::enableViewOption(const std::string& option)
+{
+	setViewOptionEnabled(option, true);
+}
+
+void LLFloaterModelPreview::disableViewOption(const std::string& option)
+{
+	setViewOptionEnabled(option, false);
+}
+
+void LLFloaterModelPreview::loadModel(S32 lod)
+{
+	mModelPreview->mLoading = true;
+
+	(new LLMeshFilePicker(mModelPreview, lod))->getFile();
+}
+
+//static
+void LLFloaterModelPreview::onImportScaleCommit(LLUICtrl*,void* userdata)
+{
+	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+	if (!fp->mModelPreview)
+	{
+		return;
+	}
+
+	fp->mModelPreview->calcResourceCost();
+	fp->mModelPreview->refresh();
+}
+//static
+void LLFloaterModelPreview::onPelvisOffsetCommit( LLUICtrl*, void* userdata )
+{
+	LLFloaterModelPreview *fp =(LLFloaterModelPreview*)userdata;
+
+	if (!fp->mModelPreview)
+	{
+		return;
+	}
+	fp->mModelPreview->calcResourceCost();
+	fp->mModelPreview->refresh();
+}
+
+//static
+void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata)
+{
+	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+	if (!fp->mModelPreview)
+	{
+		return;
+	}
+
+	fp->mModelPreview->refresh();
+}
+
+//static
+void LLFloaterModelPreview::onUploadSkinCommit(LLUICtrl*,void* userdata)
+{
+	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+	if (!fp->mModelPreview)
+	{
+		return;
+	}
+	
+	fp->mModelPreview->calcResourceCost();
+	fp->mModelPreview->refresh();
+	fp->mModelPreview->resetPreviewTarget();
+	fp->mModelPreview->clearBuffers();
+}
+
+//static
+void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+	if (!fp->mModelPreview)
+	{
+		return;
+	}
+
+	S32 which_mode = 0;
+
+	LLComboBox* combo = (LLComboBox*) ctrl;
+
+	which_mode = (NUM_LOD-1)-combo->getFirstSelectedIndex(); // combo box list of lods is in reverse order
+
+	fp->mModelPreview->setPreviewLOD(which_mode);
+}
+
+//static
+void LLFloaterModelPreview::onGenerateNormalsCommit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+
+	fp->mModelPreview->generateNormals();
+}
+
+//static
+void LLFloaterModelPreview::onExplodeCommit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterModelPreview* fp = LLFloaterModelPreview::sInstance;
+
+	fp->mModelPreview->refresh();
+}
+
+//static
+void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+
+	fp->mModelPreview->genLODs();
+}
+
+//static
+void LLFloaterModelPreview::onLODParamCommit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+	fp->mModelPreview->onLODParamCommit(false);
+}
+
+//static
+void LLFloaterModelPreview::onLODParamCommitTriangleLimit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+	fp->mModelPreview->onLODParamCommit(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLFloaterModelPreview::draw()
+{
+	LLFloater::draw();
+	LLRect r = getRect();
+
+	mModelPreview->update();
+
+	if (!mModelPreview->mLoading)
+	{
+		if ( mModelPreview->getLoadState() > LLModelLoader::ERROR_PARSING )
+		{		
+			childSetTextArg("status", "[STATUS]", getString(LLModel::getStatusString(mModelPreview->getLoadState() - LLModelLoader::ERROR_PARSING)));
+		}
+		else
+		{
+			childSetTextArg("status", "[STATUS]", getString("status_idle"));
+		}
+	}
+
+	childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost));
+	childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size()));
+
+	if (!mCurRequest.empty())
+	{
+		LLMutexLock lock(mStatusLock);
+		childSetTextArg("status", "[STATUS]", mStatusMessage);
+	}
+	else
+	{
+		childSetVisible("Simplify", true);
+		childSetVisible("simplify_cancel", false);
+		childSetVisible("Decompose", true);
+		childSetVisible("decompose_cancel", false);
+	}
+	
+	U32 resource_cost = mModelPreview->mResourceCost*10;
+
+	if (childGetValue("upload_textures").asBoolean())
+	{
+		resource_cost += mModelPreview->mTextureSet.size()*10;
+	}
+
+	childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", resource_cost));
+
+	if (mModelPreview)
+	{
+		gGL.color3f(1.f, 1.f, 1.f);
+
+		gGL.getTexUnit(0)->bind(mModelPreview);
+
+
+		LLView* preview_panel = getChild<LLView>("preview_panel");
+
+		LLRect rect = preview_panel->getRect();
+		if (rect != mPreviewRect)
+		{
+			mModelPreview->refresh();
+			mPreviewRect = preview_panel->getRect();
+		}
+
+		gGL.begin( LLRender::QUADS );
+		{
+			gGL.texCoord2f(0.f, 1.f);
+			gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop-1);
+			gGL.texCoord2f(0.f, 0.f);
+			gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom);
+			gGL.texCoord2f(1.f, 0.f);
+			gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom);
+			gGL.texCoord2f(1.f, 1.f);
+			gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1);
+		}
+		gGL.end();
+
+		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+	if (mPreviewRect.pointInRect(x, y))
+	{
+		bringToFront( x, y );
+		gFocusMgr.setMouseCapture(this);
+		gViewerWindow->hideCursor();
+		mLastMouseX = x;
+		mLastMouseY = y;
+		return TRUE;
+	}
+
+	return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+	gFocusMgr.setMouseCapture(FALSE);
+	gViewerWindow->showCursor();
+	return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleHover	(S32 x, S32 y, MASK mask)
+{
+	MASK local_mask = mask & ~MASK_ALT;
+
+	if (mModelPreview && hasMouseCapture())
+	{
+		if (local_mask == MASK_PAN)
+		{
+			// pan here
+			mModelPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+		}
+		else if (local_mask == MASK_ORBIT)
+		{
+			F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+			F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+
+			mModelPreview->rotate(yaw_radians, pitch_radians);
+		}
+		else
+		{
+
+			F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+			F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+
+			mModelPreview->rotate(yaw_radians, 0.f);
+			mModelPreview->zoom(zoom_amt);
+		}
+
+
+		mModelPreview->refresh();
+
+		LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY);
+	}
+
+	if (!mPreviewRect.pointInRect(x, y) || !mModelPreview)
+	{
+		return LLFloater::handleHover(x, y, mask);
+	}
+	else if (local_mask == MASK_ORBIT)
+	{
+		gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+	}
+	else if (local_mask == MASK_PAN)
+	{
+		gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+	}
+	else
+	{
+		gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+	}
+
+	return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+	if (mPreviewRect.pointInRect(x, y) && mModelPreview)
+	{
+		mModelPreview->zoom((F32)clicks * -0.2f);
+		mModelPreview->refresh();
+	}
+
+	return TRUE;
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data)
+{
+	if (LLConvexDecomposition::getInstance() == NULL)
+	{
+		llinfos << "convex decomposition tool is a stub on this platform. cannot get decomp." << llendl;
+		return;
+	}
+
+	if (sInstance)
+	{
+		LLCDParam* param = (LLCDParam*) data;
+		std::string name(param->mName);
+		sInstance->mDecompParams[name] = ctrl->getValue();
+
+		if (name == "Simplify Method")
+		{
+			 if (ctrl->getValue().asInteger() == 0)
+			 {
+				sInstance->childSetVisible("Retain%", true);
+				sInstance->childSetVisible("Detail Scale", false);
+			 }
+			else
+			{
+				sInstance->childSetVisible("Retain%", false);
+				sInstance->childSetVisible("Detail Scale", true);
+			}
+		}
+	}
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data)
+{
+	LLCDStageData* stage_data = (LLCDStageData*) data;
+	std::string stage = stage_data->mName;
+
+	if (sInstance)
+	{
+		if (!sInstance->mCurRequest.empty())
+		{
+			llinfos << "Decomposition request still pending." << llendl;
+			return;
+		}
+
+		if (sInstance->mModelPreview)
+		{
+			for (S32 i = 0; i < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size(); ++i)
+			{
+				LLModel* mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][i];
+				DecompRequest* request = new DecompRequest(stage, mdl);
+				sInstance->mCurRequest.insert(request);
+				gMeshRepo.mDecompThread->submitRequest(request);
+			}
+		}
+
+		if (stage == "Decompose")
+		{
+			sInstance->setStatusMessage(sInstance->getString("decomposing"));
+			sInstance->childSetVisible("Decompose", false);
+			sInstance->childSetVisible("decompose_cancel", true);
+		}
+		else if (stage == "Simplify")
+		{
+			sInstance->setStatusMessage(sInstance->getString("simplifying"));
+			sInstance->childSetVisible("Simplify", false);
+			sInstance->childSetVisible("simplify_cancel", true);
+		}
+	}
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsBrowse(LLUICtrl* ctrl, void* userdata)
+{
+	sInstance->loadModel(LLModel::LOD_PHYSICS);
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata)
+{
+	S32 which_mode = 3;
+	LLCtrlSelectionInterface* iface = sInstance->childGetSelectionInterface("physics_lod_combo");
+	if (iface)
+	{
+		which_mode = iface->getFirstSelectedIndex();
+	}
+
+	sInstance->mModelPreview->setPhysicsFromLOD(which_mode);
+}
+
+//static 
+void LLFloaterModelPreview::onCancel(LLUICtrl* ctrl, void* data)
+{
+	if (sInstance)
+	{
+		sInstance->closeFloater(false);
+	}
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data)
+{
+	if (sInstance)
+	{
+		for (std::set<LLPointer<DecompRequest> >::iterator iter = sInstance->mCurRequest.begin();
+			iter != sInstance->mCurRequest.end(); ++iter)
+		{
+		    DecompRequest* req = *iter;
+		    req->mContinue = 0;
+		}
+
+		sInstance->mCurRequest.clear();
+	}
+}
+
+void LLFloaterModelPreview::initDecompControls()
+{
+	LLSD key;
+
+	childSetCommitCallback("simplify_cancel", onPhysicsStageCancel, NULL);
+	childSetCommitCallback("decompose_cancel", onPhysicsStageCancel, NULL);
+
+	childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL);
+	childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL);
+
+	static const LLCDStageData* stage = NULL;
+	static S32 stage_count = 0;
+
+	if (!stage && LLConvexDecomposition::getInstance() != NULL)
+	{
+		stage_count = LLConvexDecomposition::getInstance()->getStages(&stage);
+	}
+
+	static const LLCDParam* param = NULL;
+	static S32 param_count = 0;
+	if (!param && LLConvexDecomposition::getInstance() != NULL)
+	{
+		param_count = LLConvexDecomposition::getInstance()->getParameters(&param);
+	}
+
+	for (S32 j = stage_count-1; j >= 0; --j)
+	{
+		LLButton* button = getChild<LLButton>(stage[j].mName);
+		if (button)
+		{
+			button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]);
+		}
+
+		gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j;
+		// protected against stub by stage_count being 0 for stub above
+		LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback);
+
+		//llinfos << "Physics decomp stage " << stage[j].mName << " (" << j << ") parameters:" << llendl;
+		//llinfos << "------------------------------------" << llendl;
+
+		for (S32 i = 0; i < param_count; ++i)
+		{
+			if (param[i].mStage != j)
+			{
+				continue;
+			}
+
+			std::string name(param[i].mName ? param[i].mName : "");
+			std::string description(param[i].mDescription ? param[i].mDescription : "");
+
+			std::string type = "unknown";
+
+			llinfos << name << " - " << description << llendl;
+
+			if (param[i].mType == LLCDParam::LLCD_FLOAT)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mFloat);
+				//llinfos << "Type: float, Default: " << param[i].mDefault.mFloat << llendl;
+
+				LLSliderCtrl* slider = getChild<LLSliderCtrl>(name);
+				if (slider)
+				{
+					slider->setMinValue(param[i].mDetails.mRange.mLow.mFloat);
+					slider->setMaxValue(param[i].mDetails.mRange.mHigh.mFloat);
+					slider->setIncrement(param[i].mDetails.mRange.mDelta.mFloat);
+					slider->setValue(param[i].mDefault.mFloat);
+					slider->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+				}
+			}
+			else if (param[i].mType == LLCDParam::LLCD_INTEGER)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue);
+				//llinfos << "Type: integer, Default: " << param[i].mDefault.mIntOrEnumValue << llendl;
+
+				LLSliderCtrl* slider = getChild<LLSliderCtrl>(name);
+				if (slider)
+				{
+					slider->setMinValue(param[i].mDetails.mRange.mLow.mIntOrEnumValue);
+					slider->setMaxValue(param[i].mDetails.mRange.mHigh.mIntOrEnumValue);
+					slider->setIncrement(param[i].mDetails.mRange.mDelta.mIntOrEnumValue);
+					slider->setValue(param[i].mDefault.mIntOrEnumValue);
+					slider->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+				}
+			}
+			else if (param[i].mType == LLCDParam::LLCD_BOOLEAN)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mBool);
+				//llinfos << "Type: boolean, Default: " << (param[i].mDefault.mBool ? "True" : "False") << llendl;
+
+				LLCheckBoxCtrl* check_box = getChild<LLCheckBoxCtrl>(name);
+				if (check_box)
+				{
+					check_box->setValue(param[i].mDefault.mBool);
+					check_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+				}
+			}
+			else if (param[i].mType == LLCDParam::LLCD_ENUM)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue);
+				//llinfos << "Type: enum, Default: " << param[i].mDefault.mIntOrEnumValue << llendl;
+
+				{ //plug into combo box
+
+					//llinfos << "Accepted values: " << llendl;
+					LLComboBox* combo_box = getChild<LLComboBox>(name);
+					for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k)
+					{
+						//llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue
+						//	<< " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl;
+
+						combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName,
+							LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue));
+					}
+					combo_box->setValue(param[i].mDefault.mIntOrEnumValue);
+					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+				}
+
+				//llinfos << "----" << llendl;
+			}
+			//llinfos << "-----------------------------" << llendl;
+		}
+	}
+
+	childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this);
+}
+
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+// static
+void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler)
+{
+	gViewerWindow->showCursor();
+}
+
+//-----------------------------------------------------------------------------
+// LLModelLoader
+//-----------------------------------------------------------------------------
+LLModelLoader::LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, 
+							  std::deque<std::string>& jointsFromNodes )
+: mJointList( jointMap )
+, mJointsFromNode( jointsFromNodes )
+, LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mFirstTransform(TRUE)
+{
+	mJointMap["mPelvis"] = "mPelvis";
+	mJointMap["mTorso"] = "mTorso";
+	mJointMap["mChest"] = "mChest";
+	mJointMap["mNeck"] = "mNeck";
+	mJointMap["mHead"] = "mHead";
+	mJointMap["mSkull"] = "mSkull";
+	mJointMap["mEyeRight"] = "mEyeRight";
+	mJointMap["mEyeLeft"] = "mEyeLeft";
+	mJointMap["mCollarLeft"] = "mCollarLeft";
+	mJointMap["mShoulderLeft"] = "mShoulderLeft";
+	mJointMap["mElbowLeft"] = "mElbowLeft";
+	mJointMap["mWristLeft"] = "mWristLeft";
+	mJointMap["mCollarRight"] = "mCollarRight";
+	mJointMap["mShoulderRight"] = "mShoulderRight";
+	mJointMap["mElbowRight"] = "mElbowRight";
+	mJointMap["mWristRight"] = "mWristRight";
+	mJointMap["mHipRight"] = "mHipRight";
+	mJointMap["mKneeRight"] = "mKneeRight";
+	mJointMap["mAnkleRight"] = "mAnkleRight";
+	mJointMap["mFootRight"] = "mFootRight";
+	mJointMap["mToeRight"] = "mToeRight";
+	mJointMap["mHipLeft"] = "mHipLeft";
+	mJointMap["mKneeLeft"] = "mKneeLeft";
+	mJointMap["mAnkleLeft"] = "mAnkleLeft";
+	mJointMap["mFootLeft"] = "mFootLeft";
+	mJointMap["mToeLeft"] = "mToeLeft";
+
+	mJointMap["avatar_mPelvis"] = "mPelvis";
+	mJointMap["avatar_mTorso"] = "mTorso";
+	mJointMap["avatar_mChest"] = "mChest";
+	mJointMap["avatar_mNeck"] = "mNeck";
+	mJointMap["avatar_mHead"] = "mHead";
+	mJointMap["avatar_mSkull"] = "mSkull";
+	mJointMap["avatar_mEyeRight"] = "mEyeRight";
+	mJointMap["avatar_mEyeLeft"] = "mEyeLeft";
+	mJointMap["avatar_mCollarLeft"] = "mCollarLeft";
+	mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft";
+	mJointMap["avatar_mElbowLeft"] = "mElbowLeft";
+	mJointMap["avatar_mWristLeft"] = "mWristLeft";
+	mJointMap["avatar_mCollarRight"] = "mCollarRight";
+	mJointMap["avatar_mShoulderRight"] = "mShoulderRight";
+	mJointMap["avatar_mElbowRight"] = "mElbowRight";
+	mJointMap["avatar_mWristRight"] = "mWristRight";
+	mJointMap["avatar_mHipRight"] = "mHipRight";
+	mJointMap["avatar_mKneeRight"] = "mKneeRight";
+	mJointMap["avatar_mAnkleRight"] = "mAnkleRight";
+	mJointMap["avatar_mFootRight"] = "mFootRight";
+	mJointMap["avatar_mToeRight"] = "mToeRight";
+	mJointMap["avatar_mHipLeft"] = "mHipLeft";
+	mJointMap["avatar_mKneeLeft"] = "mKneeLeft";
+	mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft";
+	mJointMap["avatar_mFootLeft"] = "mFootLeft";
+	mJointMap["avatar_mToeLeft"] = "mToeLeft";
+
+
+	mJointMap["hip"] = "mPelvis";
+	mJointMap["abdomen"] = "mTorso";
+	mJointMap["chest"] = "mChest";
+	mJointMap["neck"] = "mNeck";
+	mJointMap["head"] = "mHead";
+	mJointMap["figureHair"] = "mSkull";
+	mJointMap["lCollar"] = "mCollarLeft";
+	mJointMap["lShldr"] = "mShoulderLeft";
+	mJointMap["lForeArm"] = "mElbowLeft";
+	mJointMap["lHand"] = "mWristLeft";
+	mJointMap["rCollar"] = "mCollarRight";
+	mJointMap["rShldr"] = "mShoulderRight";
+	mJointMap["rForeArm"] = "mElbowRight";
+	mJointMap["rHand"] = "mWristRight";
+	mJointMap["rThigh"] = "mHipRight";
+	mJointMap["rShin"] = "mKneeRight";
+	mJointMap["rFoot"] = "mFootRight";
+	mJointMap["lThigh"] = "mHipLeft";
+	mJointMap["lShin"] = "mKneeLeft";
+	mJointMap["lFoot"] = "mFootLeft";
+
+	if (mPreview)
+	{
+		//only try to load from slm if viewer is configured to do so and this is the 
+		//initial model load (not an LoD or physics shape)
+		mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mPreview->mUploadData.empty();
+		mPreview->setLoadState(STARTING);
+	}
+	else
+	{
+		mTrySLM = false;
+	}
+
+	assert_main_thread();
+	sActiveLoaderList.push_back(this) ;
+}
+
+LLModelLoader::~LLModelLoader()
+{
+	assert_main_thread();
+	sActiveLoaderList.remove(this);
+}
+
+void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
+{
+	LLVector4a box[] =
+	{
+		LLVector4a(-1, 1,-1),
+		LLVector4a(-1, 1, 1),
+		LLVector4a(-1,-1,-1),
+		LLVector4a(-1,-1, 1),
+		LLVector4a( 1, 1,-1),
+		LLVector4a( 1, 1, 1),
+		LLVector4a( 1,-1,-1),
+		LLVector4a( 1,-1, 1),
+	};
+
+	for (S32 j = 0; j < model->getNumVolumeFaces(); ++j)
+	{
+		const LLVolumeFace& face = model->getVolumeFace(j);
+
+		LLVector4a center;
+		center.setAdd(face.mExtents[0], face.mExtents[1]);
+		center.mul(0.5f);
+		LLVector4a size;
+		size.setSub(face.mExtents[1],face.mExtents[0]);
+		size.mul(0.5f);
+
+		for (U32 i = 0; i < 8; i++)
+		{
+			LLVector4a t;
+			t.setMul(size, box[i]);
+			t.add(center);
+
+			LLVector4a v;
+
+			mat.affineTransform(t, v);
+
+			if (first_transform)
+			{
+				first_transform = FALSE;
+				min = max = v;
+			}
+			else
+			{
+				update_min_max(min, max, v);
+			}
+		}
+	}
+}
+
+void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform)
+{
+	LLVector4a mina, maxa;
+	LLMatrix4a mata;
+
+	mata.loadu(mat);
+	mina.load3(min.mV);
+	maxa.load3(max.mV);
+
+	stretch_extents(model, mata, mina, maxa, first_transform);
+
+	min.set(mina.getF32ptr());
+	max.set(maxa.getF32ptr());
+}
+
+void LLModelLoader::run()
+{
+	if (!doLoadModel())
+	{
+		mPreview = NULL;
+	}
+
+	doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
+}
+
+bool LLModelLoader::doLoadModel()
+{
+	//first, look for a .slm file of the same name that was modified later
+	//than the .dae
+
+	if (mTrySLM)
+	{
+		std::string filename = mFilename;
+			
+		std::string::size_type i = filename.rfind(".");
+		if (i != std::string::npos)
+		{
+			filename.replace(i, filename.size()-1, ".slm");
+			llstat slm_status;
+			if (LLFile::stat(filename, &slm_status) == 0)
+			{ //slm file exists
+				llstat dae_status;
+				if (LLFile::stat(mFilename, &dae_status) != 0 ||
+					dae_status.st_mtime < slm_status.st_mtime)
+				{
+					if (loadFromSLM(filename))
+					{ //slm successfully loaded, if this fails, fall through and
+						//try loading from dae
+
+						mLod = -1; //successfully loading from an slm implicitly sets all 
+									//LoDs
+						return true;
+					}
+				}
+			}	
+		}
+	}
+
+	//no suitable slm exists, load from the .dae file
+	DAE dae;
+	domCOLLADA* dom = dae.open(mFilename);
+	
+	if (!dom)
+	{
+		return false;
+	}
+
+	daeDatabase* db = dae.getDatabase();
+	
+	daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+	
+	daeDocument* doc = dae.getDoc(mFilename);
+	if (!doc)
+	{
+		llwarns << "can't find internal doc" << llendl;
+		return false;
+	}
+	
+	daeElement* root = doc->getDomRoot();
+	if (!root)
+	{
+		llwarns << "document has no root" << llendl;
+		return false;
+	}
+	
+	//get unit scale
+	mTransform.setIdentity();
+	
+	domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID())));
+	
+	if (unit)
+	{
+		F32 meter = unit->getMeter();
+		mTransform.mMatrix[0][0] = meter;
+		mTransform.mMatrix[1][1] = meter;
+		mTransform.mMatrix[2][2] = meter;
+	}
+	
+	//get up axis rotation
+	LLMatrix4 rotation;
+	
+	domUpAxisType up = UPAXISTYPE_Y_UP;  // default is Y_UP
+	domAsset::domUp_axis* up_axis =
+	daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID())));
+	
+	if (up_axis)
+	{
+		up = up_axis->getValue();
+	}
+	
+	if (up == UPAXISTYPE_X_UP)
+	{
+		rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
+	}
+	else if (up == UPAXISTYPE_Y_UP)
+	{
+		rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
+	}
+	
+	rotation *= mTransform;
+	mTransform = rotation;
+	
+	
+	for (daeInt idx = 0; idx < count; ++idx)
+	{ //build map of domEntities to LLModel
+		domMesh* mesh = NULL;
+		db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH);
+		
+		if (mesh)
+		{
+			LLPointer<LLModel> model = LLModel::loadModelFromDomMesh(mesh);
+			
+			if(model->getStatus() != LLModel::NO_ERRORS)
+			{
+				setLoadState(ERROR_PARSING + model->getStatus()) ;
+				return true ; //abort
+			}
+
+			if (model.notNull() && validate_model(model))
+			{
+				mModelList.push_back(model);
+				mModel[mesh] = model;
+			}
+		}
+	}
+	
+	count = db->getElementCount(NULL, COLLADA_TYPE_SKIN);
+	for (daeInt idx = 0; idx < count; ++idx)
+	{ //add skinned meshes as instances
+		domSkin* skin = NULL;
+		db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN);
+		
+		if (skin)
+		{
+			domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement());
+			
+			if (geom)
+			{
+				domMesh* mesh = geom->getMesh();
+				if (mesh)
+				{
+					LLModel* model = mModel[mesh];
+					if (model)
+					{
+						LLVector3 mesh_scale_vector;
+						LLVector3 mesh_translation_vector;
+						model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+						
+						LLMatrix4 normalized_transformation;
+						normalized_transformation.setTranslation(mesh_translation_vector);
+						
+						LLMatrix4 mesh_scale;
+						mesh_scale.initScale(mesh_scale_vector);
+						mesh_scale *= normalized_transformation;
+						normalized_transformation = mesh_scale;
+						
+						glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix);
+						inv_mat = inv_mat.inverse();
+						LLMatrix4 inverse_normalized_transformation(inv_mat.m);
+						
+						domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix();
+						
+						if (bind_mat)
+						{ //get bind shape matrix
+							domFloat4x4& dom_value = bind_mat->getValue();
+							
+							LLMeshSkinInfo& skin_info = model->mSkinInfo;
+
+							for (int i = 0; i < 4; i++)
+							{
+								for(int j = 0; j < 4; j++)
+								{
+									skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4];
+								}
+							}
+							
+							LLMatrix4 trans = normalized_transformation;
+							trans *= skin_info.mBindShapeMatrix;
+							skin_info.mBindShapeMatrix = trans;
+							
+						}
+										
+											
+						//Some collada setup for accessing the skeleton
+						daeElement* pElement = 0;
+						dae.getDatabase()->getElement( &pElement, 0, 0, "skeleton" );
+						
+						//Try to get at the skeletal instance controller
+						domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement );
+						bool missingSkeletonOrScene = false;
+						
+						//If no skeleton, do a breadth-first search to get at specific joints
+						bool rootNode = false;
+						bool skeletonWithNoRootNode = false;
+						
+						//Need to test for a skeleton that does not have a root node
+						//This occurs when your instance controller does not have an associated scene 
+						if ( pSkeleton )
+						{
+							daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
+							if ( pSkeletonRootNode )
+							{
+								rootNode = true;
+							}
+							else 
+							{
+								skeletonWithNoRootNode = true;
+							}
+
+						}
+						if ( !pSkeleton || !rootNode )
+						{
+							daeElement* pScene = root->getDescendant("visual_scene");
+							if ( !pScene )
+							{
+								llwarns<<"No visual scene - unable to parse bone offsets "<<llendl;
+								missingSkeletonOrScene = true;
+							}
+							else
+							{
+								//Get the children at this level
+								daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren();
+								S32 childCount = children.getCount();
+								
+								//Process any children that are joints
+								//Not all children are joints, some code be ambient lights, cameras, geometry etc..
+								for (S32 i = 0; i < childCount; ++i)
+								{
+									domNode* pNode = daeSafeCast<domNode>(children[i]);
+									if ( isNodeAJoint( pNode ) )
+									{
+										processJointNode( pNode, mJointList );
+									}
+								}
+							}
+						}
+						else
+							//Has Skeleton
+						{
+							//Get the root node of the skeleton
+							daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
+							if ( pSkeletonRootNode )
+							{
+								//Once we have the root node - start acccessing it's joint components
+								const int jointCnt = mJointMap.size();
+								std::map<std::string, std::string> :: const_iterator jointIt = mJointMap.begin();
+								
+								//Loop over all the possible joints within the .dae - using the allowed joint list in the ctor.
+								for ( int i=0; i<jointCnt; ++i, ++jointIt )
+								{
+									//Build a joint for the resolver to work with
+									char str[64]={0};
+									sprintf(str,"./%s",(*jointIt).second.c_str() );
+									//llwarns<<"Joint "<< str <<llendl;
+									
+									//Setup the resolver
+                                    daeSIDResolver resolver( pSkeletonRootNode, str );
+									
+                                    //Look for the joint
+                                    domNode* pJoint = daeSafeCast<domNode>( resolver.getElement() );
+                                    if ( pJoint )
+                                    {
+										//Pull out the translate id and store it in the jointTranslations map
+										daeSIDResolver jointResolver( pJoint, "./translate" );
+										domTranslate* pTranslate = daeSafeCast<domTranslate>( jointResolver.getElement() );
+										
+										LLMatrix4 workingTransform;
+										
+										//Translation via SID
+										if ( pTranslate )
+										{
+											extractTranslation( pTranslate, workingTransform );
+										}
+										else
+										{
+											//Translation via child from element
+											daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" );
+											if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() )
+											{
+												llwarns<< "The found element is not a translate node" <<llendl;
+												missingSkeletonOrScene = true;
+											}
+											else
+											{
+												extractTranslationViaElement( pTranslateElement, workingTransform );
+											}
+										}
+										
+										//Store the joint transform w/respect to it's name.
+										mJointList[(*jointIt).second.c_str()] = workingTransform;
+                                    }
+								}
+								
+								//If anything failed in regards to extracting the skeleton, joints or translation id,
+								//mention it
+								if ( missingSkeletonOrScene  )
+								{
+									llwarns<< "Partial jointmap found in asset - did you mean to just have a partial map?" << llendl;
+								}
+							}//got skeleton?
+						}
+						
+						
+						domSkin::domJoints* joints = skin->getJoints();
+						
+						domInputLocal_Array& joint_input = joints->getInput_array();
+						
+						for (size_t i = 0; i < joint_input.getCount(); ++i)
+						{
+							domInputLocal* input = joint_input.get(i);
+							xsNMTOKEN semantic = input->getSemantic();
+							
+							if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0)
+							{ //found joint source, fill model->mJointMap and model->mSkinInfo.mJointNames
+								daeElement* elem = input->getSource().getElement();
+								
+								domSource* source = daeSafeCast<domSource>(elem);
+								if (source)
+								{
+									
+									
+									domName_array* names_source = source->getName_array();
+									
+									if (names_source)
+									{
+										domListOfNames &names = names_source->getValue();
+										
+										for (size_t j = 0; j < names.getCount(); ++j)
+										{
+											std::string name(names.get(j));
+											if (mJointMap.find(name) != mJointMap.end())
+											{
+												name = mJointMap[name];
+											}
+											model->mSkinInfo.mJointNames.push_back(name);
+											model->mSkinInfo.mJointMap[name] = j;
+										}
+									}
+									else
+									{
+										domIDREF_array* names_source = source->getIDREF_array();
+										if (names_source)
+										{
+											xsIDREFS& names = names_source->getValue();
+											
+											for (size_t j = 0; j < names.getCount(); ++j)
+											{
+												std::string name(names.get(j).getID());
+												if (mJointMap.find(name) != mJointMap.end())
+												{
+													name = mJointMap[name];
+												}
+												model->mSkinInfo.mJointNames.push_back(name);
+												model->mSkinInfo.mJointMap[name] = j;
+											}
+										}
+									}
+								}
+							}
+							else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0)
+							{ //found inv_bind_matrix array, fill model->mInvBindMatrix
+								domSource* source = daeSafeCast<domSource>(input->getSource().getElement());
+								if (source)
+								{
+									domFloat_array* t = source->getFloat_array();
+									if (t)
+									{
+										domListOfFloats& transform = t->getValue();
+										S32 count = transform.getCount()/16;
+										
+										for (S32 k = 0; k < count; ++k)
+										{
+											LLMatrix4 mat;
+											
+											for (int i = 0; i < 4; i++)
+											{
+												for(int j = 0; j < 4; j++)
+												{
+													mat.mMatrix[i][j] = transform[k*16 + i + j*4];
+												}
+											}
+											
+											model->mSkinInfo.mInvBindMatrix.push_back(mat);
+										}
+									}
+								}
+							}
+						}
+						
+						//Now that we've parsed the joint array, let's determine if we have a full rig
+						//(which means we have all the joints that are required for an avatar versus
+						//a skinned asset attached to a node in a file that contains an entire skeleton,
+						//but does not use the skeleton).						
+						buildJointToNodeMappingFromScene( root );
+						mPreview->critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames );
+										
+						if ( !missingSkeletonOrScene )
+						{
+							//Set the joint translations on the avatar - if it's a full mapping
+							//The joints are reset in the dtor
+							if ( mPreview->getRigWithSceneParity() )
+							{	
+								std::map<std::string, std::string> :: const_iterator masterJointIt = mJointMap.begin();
+								std::map<std::string, std::string> :: const_iterator masterJointItEnd = mJointMap.end();
+								for (;masterJointIt!=masterJointItEnd;++masterJointIt )
+								{
+									std::string lookingForJoint = (*masterJointIt).first.c_str();
+									
+									if ( mJointList.find( lookingForJoint ) != mJointList.end() )
+									{
+										//llinfos<<"joint "<<lookingForJoint.c_str()<<llendl;
+										LLMatrix4 jointTransform = mJointList[lookingForJoint];
+										LLJoint* pJoint = gAgentAvatarp->getJoint( lookingForJoint );
+										if ( pJoint )
+										{   
+											pJoint->storeCurrentXform( jointTransform.getTranslation() );												
+										}
+										else
+										{
+											//Most likely an error in the asset.
+											llwarns<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << llendl;
+										}
+									}
+								}
+							}
+						} //missingSkeletonOrScene
+						
+						
+						//We need to construct the alternate bind matrix (which contains the new joint positions)
+						//in the same order as they were stored in the joint buffer. The joints associated
+						//with the skeleton are not stored in the same order as they are in the exported joint buffer.
+						//This remaps the skeletal joints to be in the same order as the joints stored in the model.
+						std::vector<std::string> :: const_iterator jointIt  = model->mSkinInfo.mJointNames.begin();
+						const int jointCnt = model->mSkinInfo.mJointNames.size();
+						for ( int i=0; i<jointCnt; ++i, ++jointIt )
+						{
+							std::string lookingForJoint = (*jointIt).c_str();
+							//Look for the joint xform that we extracted from the skeleton, using the jointIt as the key
+							//and store it in the alternate bind matrix
+							if ( mJointList.find( lookingForJoint ) != mJointList.end() )
+							{
+								LLMatrix4 jointTransform = mJointList[lookingForJoint];
+								LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i];
+								newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() );
+								model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse );
+							}
+							else
+							{
+								llwarns<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<llendl;
+							}
+						}
+						
+						//grab raw position array
+						
+						domVertices* verts = mesh->getVertices();
+						if (verts)
+						{
+							domInputLocal_Array& inputs = verts->getInput_array();
+							for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i)
+							{
+								if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0)
+								{
+									domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
+									if (pos_source)
+									{
+										domFloat_array* pos_array = pos_source->getFloat_array();
+										if (pos_array)
+										{
+											domListOfFloats& pos = pos_array->getValue();
+											
+											for (size_t j = 0; j < pos.getCount(); j += 3)
+											{
+												if (pos.getCount() <= j+2)
+												{
+													llerrs << "WTF?" << llendl;
+												}
+												
+												LLVector3 v(pos[j], pos[j+1], pos[j+2]);
+												
+												//transform from COLLADA space to volume space
+												v = v * inverse_normalized_transformation;
+												
+												model->mPosition.push_back(v);
+											}
+										}
+									}
+								}
+							}
+						}
+						
+						//grab skin weights array
+						domSkin::domVertex_weights* weights = skin->getVertex_weights();
+						if (weights)
+						{
+							domInputLocalOffset_Array& inputs = weights->getInput_array();
+							domFloat_array* vertex_weights = NULL;
+							for (size_t i = 0; i < inputs.getCount(); ++i)
+							{
+								if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0)
+								{
+									domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
+									if (weight_source)
+									{
+										vertex_weights = weight_source->getFloat_array();
+									}
+								}
+							}
+							
+							if (vertex_weights)
+							{
+								domListOfFloats& w = vertex_weights->getValue();
+								domListOfUInts& vcount = weights->getVcount()->getValue();
+								domListOfInts& v = weights->getV()->getValue();
+								
+								U32 c_idx = 0;
+								for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx)
+								{ //for each vertex
+									daeUInt count = vcount[vc_idx];
+									
+									//create list of weights that influence this vertex
+									LLModel::weight_list weight_list;
+									
+									for (daeUInt i = 0; i < count; ++i)
+									{ //for each weight
+										daeInt joint_idx = v[c_idx++];
+										daeInt weight_idx = v[c_idx++];
+										
+										if (joint_idx == -1)
+										{
+											//ignore bindings to bind_shape_matrix
+											continue;
+										}
+										
+										F32 weight_value = w[weight_idx];
+										
+										weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value));
+									}
+									
+									//sort by joint weight
+									std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
+									
+									std::vector<LLModel::JointWeight> wght;
+									
+									F32 total = 0.f;
+									
+									for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i)
+									{ //take up to 4 most significant weights
+										if (weight_list[i].mWeight > 0.f)
+										{
+											wght.push_back( weight_list[i] );
+											total += weight_list[i].mWeight;
+										}
+									}
+									
+									F32 scale = 1.f/total;
+									if (scale != 1.f)
+									{ //normalize weights
+										for (U32 i = 0; i < wght.size(); ++i)
+										{
+											wght[i].mWeight *= scale;
+										}
+									}
+									
+									model->mSkinWeights[model->mPosition[vc_idx]] = wght;
+								}
+								
+								//add instance to scene for this model
+								
+								LLMatrix4 transformation = mTransform;
+								// adjust the transformation to compensate for mesh normalization
+								
+								LLMatrix4 mesh_translation;
+								mesh_translation.setTranslation(mesh_translation_vector);
+								mesh_translation *= transformation;
+								transformation = mesh_translation;
+								
+								LLMatrix4 mesh_scale;
+								mesh_scale.initScale(mesh_scale_vector);
+								mesh_scale *= transformation;
+								transformation = mesh_scale;
+								
+								std::vector<LLImportMaterial> materials;
+								materials.resize(model->getNumVolumeFaces());
+								mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
+								stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	daeElement* scene = root->getDescendant("visual_scene");
+	
+	if (!scene)
+	{
+		llwarns << "document has no visual_scene" << llendl;
+		setLoadState( ERROR_PARSING );
+		return false;
+	}
+	setLoadState( DONE );
+
+	processElement(scene);
+	
+	return true;
+}
+
+void LLModelLoader::setLoadState(U32 state)
+{
+	if (mPreview)
+	{
+		mPreview->setLoadState(state);
+	}
+}
+
+bool LLModelLoader::loadFromSLM(const std::string& filename)
+{ 
+	//only need to populate mScene with data from slm
+	llstat stat;
+
+	if (LLFile::stat(filename, &stat))
+	{ //file does not exist
+		return false;
+	}
+
+	S32 file_size = (S32) stat.st_size;
+	
+	llifstream ifstream(filename, std::ifstream::in | std::ifstream::binary);
+	LLSD data;
+	LLSDSerialize::fromBinary(data, ifstream, file_size);
+	ifstream.close();
+
+	//build model list for each LoD
+	model_list model[LLModel::NUM_LODS];
+
+	LLSD& mesh = data["mesh"];
+
+	LLVolumeParams volume_params;
+	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+	for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+	{
+		for (U32 i = 0; i < mesh.size(); ++i)
+		{
+			std::stringstream str(mesh[i].asString());
+			LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod);
+			if (loaded_model->loadModel(str))
+			{
+				loaded_model->mLocalID = i;
+				model[lod].push_back(loaded_model);
+
+				if (lod == LLModel::LOD_HIGH && !loaded_model->mSkinInfo.mJointNames.empty())
+				{ 
+					//check to see if rig is valid					
+					mPreview->critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );					
+				}
+			}
+			else
+			{
+				llassert(model[lod].empty());
+			}
+		}
+	}	
+
+	if (model[LLModel::LOD_HIGH].empty())
+	{ //failed to load high lod
+		return false;
+	}
+
+	//load instance list
+	model_instance_list instance_list;
+
+	LLSD& instance = data["instance"];
+
+	for (U32 i = 0; i < instance.size(); ++i)
+	{
+		//deserialize instance list
+		instance_list.push_back(LLModelInstance(instance[i]));
+
+		//match up model instance pointers
+		S32 idx = instance_list[i].mLocalMeshID;
+
+		for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+		{
+			if (!model[lod].empty())
+			{
+				instance_list[i].mLOD[lod] = model[lod][idx];
+			}
+		}
+
+		instance_list[i].mModel = model[LLModel::LOD_HIGH][idx];
+	}
+
+
+	//convert instance_list to mScene
+	mFirstTransform = TRUE;
+	for (U32 i = 0; i < instance_list.size(); ++i)
+	{
+		LLModelInstance& cur_instance = instance_list[i];
+		mScene[cur_instance.mTransform].push_back(cur_instance);
+		stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform);
+	}
+	
+	setLoadState( DONE );
+
+	return true;
+}
+
+//static
+bool LLModelLoader::isAlive(LLModelLoader* loader)
+{
+	if(!loader)
+	{
+		return false ;
+	}
+
+	std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ;
+	for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ;
+	
+	return *iter == loader ;
+}
+
+void LLModelLoader::loadModelCallback()
+{
+	assert_main_thread();
+
+	if (mPreview)
+	{
+		mPreview->loadModelCallback(mLod);	
+	}
+
+	while (!isStopped())
+	{ //wait until this thread is stopped before deleting self
+		apr_sleep(100);
+	}
+
+	//doubel check if "this" is valid before deleting it, in case it is aborted during running.
+	if(!isAlive(this))
+	{
+		return ;
+	}
+
+	//cleanup model loader
+	if (mPreview)
+	{
+		mPreview->mModelLoader = NULL;
+	}
+
+	delete this;
+}
+//-----------------------------------------------------------------------------
+// buildJointToNodeMappingFromScene()
+//-----------------------------------------------------------------------------
+void LLModelLoader::buildJointToNodeMappingFromScene( daeElement* pRoot )
+{
+	daeElement* pScene = pRoot->getDescendant("visual_scene");
+	if ( pScene )
+	{
+		daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren();
+		S32 childCount = children.getCount();
+		for (S32 i = 0; i < childCount; ++i)
+		{
+			domNode* pNode = daeSafeCast<domNode>(children[i]);
+			processJointToNodeMapping( pNode );			
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+// processJointToNodeMapping()
+//-----------------------------------------------------------------------------
+void LLModelLoader::processJointToNodeMapping( domNode* pNode )
+{
+	if ( isNodeAJoint( pNode ) )
+	{
+		//1.Store the parent
+		std::string nodeName = pNode->getName();
+		if ( !nodeName.empty() )
+		{
+			mJointsFromNode.push_front( pNode->getName() );
+		}
+		//2. Handle the kiddo's
+		daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren();
+		S32 childOfChildCount = childOfChild.getCount();
+		for (S32 i = 0; i < childOfChildCount; ++i)
+		{
+			domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] );
+			if ( pChildNode )
+			{
+				processJointToNodeMapping( pChildNode );
+			}
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// critiqueRigForUploadApplicability()
+//-----------------------------------------------------------------------------
+void LLModelPreview::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset )
+{
+	critiqueJointToNodeMappingFromScene();
+	
+	//Determines the following use cases for a rig:
+	//1. It is suitable for upload with skin weights & joint positions, or
+	//2. It is suitable for upload as standard av with just skin weights
+	
+	bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset );
+	bool isRigLegacyOK			 = isRigLegacy( jointListFromAsset );
+
+	//It's OK that both could end up being true, both default to false
+	if ( isJointPositionUploadOK )
+	{
+		setRigValidForJointPositionUpload( true );
+	}
+
+	if ( isRigLegacyOK )
+	{
+		setLegacyRigValid( true );
+	}
+
+	if ( getRigWithSceneParity() && isJointPositionUploadOK )
+	{
+		setResetJointFlag( true );
+	}
+}
+//-----------------------------------------------------------------------------
+// critiqueJointToNodeMappingFromScene()
+//-----------------------------------------------------------------------------
+void LLModelPreview::critiqueJointToNodeMappingFromScene( void  )
+{
+	//Do the actual nodes back the joint listing from the dae?
+	//if yes then this is a fully rigged asset, otherwise it's just a partial rig
+	
+	std::deque<std::string>::iterator jointsFromNodeIt = mJointsFromNode.begin();
+	std::deque<std::string>::iterator jointsFromNodeEndIt = mJointsFromNode.end();
+	bool result = true;
+
+	if ( !mJointsFromNode.empty() )
+	{
+		for ( ;jointsFromNodeIt!=jointsFromNodeEndIt;++jointsFromNodeIt )
+		{
+			std::string name = *jointsFromNodeIt;
+			if ( mJointTransformMap.find( name ) != mJointTransformMap.end() )
+			{
+				continue;
+			}
+			else
+			{
+				llinfos<<"critiqueJointToNodeMappingFromScene is missing a: "<<name<<llendl;
+				result = false;				
+			}
+		}
+	}
+	else
+	{
+		result = false;
+	}
+
+	//Determines the following use cases for a rig:
+	//1. Full av rig  w/1-1 mapping from the scene and joint array
+	//2. Partial rig but w/o parity between the scene and joint array
+	if ( result )
+	{		
+		setResetJointFlag( true );
+		setRigWithSceneParity( true );
+	}
+	else
+	{
+		setResetJointFlag( false );
+	}	
+}
+//-----------------------------------------------------------------------------
+// isRigLegacy()
+//-----------------------------------------------------------------------------
+bool LLModelPreview::isRigLegacy( const std::vector<std::string> &jointListFromAsset )
+{
+	//No joints in asset
+	if ( jointListFromAsset.size() == 0 )
+	{
+		return false;
+	}
+
+	bool result = false;
+
+	std::deque<std::string> :: const_iterator masterJointIt = mMasterLegacyJointList.begin();	
+	std::deque<std::string> :: const_iterator masterJointEndIt = mMasterLegacyJointList.end();
+	
+	std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();	
+	std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
+	
+	for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
+	{
+		result = false;
+		modelJointIt = jointListFromAsset.begin();
+
+		for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
+		{
+			if ( *masterJointIt == *modelJointIt )
+			{
+				result = true;
+				break;
+			}			
+		}		
+		if ( !result )
+		{
+			llinfos<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< llendl;
+			break;
+		}
+	}	
+	return result;
+}
+//-----------------------------------------------------------------------------
+// isRigSuitableForJointPositionUpload()
+//-----------------------------------------------------------------------------
+bool LLModelPreview::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset )
+{
+	bool result = false;
+
+	std::deque<std::string> :: const_iterator masterJointIt = mMasterJointList.begin();	
+	std::deque<std::string> :: const_iterator masterJointEndIt = mMasterJointList.end();
+	
+	std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();	
+	std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
+	
+	for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
+	{
+		result = false;
+		modelJointIt = jointListFromAsset.begin();
+
+		for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
+		{
+			if ( *masterJointIt == *modelJointIt )
+			{
+				result = true;
+				break;
+			}			
+		}		
+		if ( !result )
+		{
+			llinfos<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< llendl;
+			break;
+		}
+	}	
+	return result;
+}
+
+
+//called in the main thread
+void LLModelLoader::loadTextures()
+{
+	BOOL is_paused = isPaused() ;
+	pause() ; //pause the loader 
+
+	for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter)
+	{
+		for(U32 i = 0 ; i < iter->second.size(); i++)
+		{
+			for(U32 j = 0 ; j < iter->second[i].mMaterial.size() ; j++)
+			{
+				if(!iter->second[i].mMaterial[j].mDiffuseMapFilename.empty())
+				{
+					iter->second[i].mMaterial[j].mDiffuseMap = 
+						LLViewerTextureManager::getFetchedTextureFromUrl("file://" + iter->second[i].mMaterial[j].mDiffuseMapFilename, TRUE, LLViewerTexture::BOOST_PREVIEW);
+					iter->second[i].mMaterial[j].mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, mPreview, NULL, FALSE);
+					iter->second[i].mMaterial[j].mDiffuseMap->forceToSaveRawImage();
+				}
+			}
+		}
+	}
+
+	if(!is_paused)
+	{
+		unpause() ;
+	}
+}
+
+//-----------------------------------------------------------------------------
+// isNodeAJoint()
+//-----------------------------------------------------------------------------
+bool LLModelLoader::isNodeAJoint( domNode* pNode )
+{
+	if ( !pNode || pNode->getName() == NULL)
+	{
+		return false;
+	}
+
+	if ( mJointMap.find( pNode->getName() )  != mJointMap.end() )
+	{
+		return true;
+	}
+
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// extractTranslation()
+//-----------------------------------------------------------------------------
+void LLModelLoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform )
+{
+	domFloat3 jointTrans = pTranslate->getValue();
+	LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] );
+	transform.setTranslation( singleJointTranslation );
+}
+//-----------------------------------------------------------------------------
+// extractTranslationViaElement()
+//-----------------------------------------------------------------------------
+void LLModelLoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform )
+{
+	domTranslate* pTranslateChild = dynamic_cast<domTranslate*>( pTranslateElement );
+	domFloat3 translateChild = pTranslateChild->getValue();
+	LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] );
+	transform.setTranslation( singleJointTranslation );
+}
+//-----------------------------------------------------------------------------
+// processJointNode()
+//-----------------------------------------------------------------------------
+void LLModelLoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms )
+{
+	if (pNode->getName() == NULL)
+	{
+		llwarns << "nameless node, can't process" << llendl;
+		return;
+	}
+
+	//llwarns<<"ProcessJointNode# Node:" <<pNode->getName()<<llendl;
+
+	//1. handle the incoming node - extract out translation via SID or element
+
+	LLMatrix4 workingTransform;
+
+	//Pull out the translate id and store it in the jointTranslations map
+	daeSIDResolver jointResolver( pNode, "./translate" );
+	domTranslate* pTranslate = daeSafeCast<domTranslate>( jointResolver.getElement() );
+
+	//Translation via SID was successful
+	if ( pTranslate )
+	{
+		extractTranslation( pTranslate, workingTransform );
+	}
+	else
+	{
+		//Translation via child from element
+		daeElement* pTranslateElement = getChildFromElement( pNode, "translate" );
+		if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() )
+		{
+			//llwarns<< "The found element is not a translate node" <<llendl;
+			daeSIDResolver jointResolver( pNode, "./matrix" );
+			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
+			if ( pMatrix )
+			{
+				//llinfos<<"A matrix SID was however found!"<<llendl;
+				domFloat4x4 domArray = pMatrix->getValue();									
+				for ( int i = 0; i < 4; i++ )
+				{
+					for( int j = 0; j < 4; j++ )
+					{
+						workingTransform.mMatrix[i][j] = domArray[i + j*4];
+					}
+				}
+			}
+			else
+			{
+				llwarns<< "The found element is not translate or matrix node - most likely a corrupt export!" <<llendl;
+			}
+		}
+		else
+		{
+			extractTranslationViaElement( pTranslateElement, workingTransform );
+		}
+	}
+
+	//Store the working transform relative to the nodes name.
+	jointTransforms[ pNode->getName() ] = workingTransform;
+
+	//2. handle the nodes children
+
+	//Gather and handle the incoming nodes children
+	daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren();
+	S32 childOfChildCount = childOfChild.getCount();
+
+	for (S32 i = 0; i < childOfChildCount; ++i)
+	{
+		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] );
+		if ( pChildNode )
+		{
+			processJointNode( pChildNode, jointTransforms );
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+// getChildFromElement()
+//-----------------------------------------------------------------------------
+daeElement* LLModelLoader::getChildFromElement( daeElement* pElement, std::string const & name )
+{
+    daeElement* pChildOfElement = pElement->getChild( name.c_str() );
+	if ( pChildOfElement )
+	{
+		return pChildOfElement;
+	}
+	llwarns<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << llendl;
+    return NULL;
+}
+
+void LLModelLoader::processElement(daeElement* element)
+{
+	LLMatrix4 saved_transform = mTransform;
+
+	domTranslate* translate = daeSafeCast<domTranslate>(element);
+	if (translate)
+	{
+		domFloat3 dom_value = translate->getValue();
+
+		LLMatrix4 translation;
+		translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+		translation *= mTransform;
+		mTransform = translation;
+	}
+
+	domRotate* rotate = daeSafeCast<domRotate>(element);
+	if (rotate)
+	{
+		domFloat4 dom_value = rotate->getValue();
+
+		LLMatrix4 rotation;
+		rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0));
+
+		rotation *= mTransform;
+		mTransform = rotation;
+	}
+
+	domScale* scale = daeSafeCast<domScale>(element);
+	if (scale)
+	{
+		domFloat3 dom_value = scale->getValue();
+
+		LLMatrix4 scaling;
+		scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+		scaling *= mTransform;
+		mTransform = scaling;
+	}
+
+	domMatrix* matrix = daeSafeCast<domMatrix>(element);
+	if (matrix)
+	{
+		domFloat4x4 dom_value = matrix->getValue();
+
+		LLMatrix4 matrix_transform;
+
+		for (int i = 0; i < 4; i++)
+		{
+			for(int j = 0; j < 4; j++)
+			{
+				matrix_transform.mMatrix[i][j] = dom_value[i + j*4];
+			}
+		}
+
+		matrix_transform *= mTransform;
+		mTransform = matrix_transform;
+	}
+
+	domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element);
+	if (instance_geo)
+	{
+		domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement());
+		if (geo)
+		{
+			domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())));
+			if (mesh)
+			{
+				LLModel* model = mModel[mesh];
+				if (model)
+				{
+					LLMatrix4 transformation = mTransform;
+
+					std::vector<LLImportMaterial> materials = getMaterials(model, instance_geo);
+
+					// adjust the transformation to compensate for mesh normalization
+					LLVector3 mesh_scale_vector;
+					LLVector3 mesh_translation_vector;
+					model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+					LLMatrix4 mesh_translation;
+					mesh_translation.setTranslation(mesh_translation_vector);
+					mesh_translation *= transformation;
+					transformation = mesh_translation;
+
+					LLMatrix4 mesh_scale;
+					mesh_scale.initScale(mesh_scale_vector);
+					mesh_scale *= transformation;
+					transformation = mesh_scale;
+
+					std::string label = getElementLabel(instance_geo);
+					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
+
+					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+				}
+			}
+		}
+	}
+
+	domInstance_node* instance_node = daeSafeCast<domInstance_node>(element);
+	if (instance_node)
+	{
+		daeElement* instance = instance_node->getUrl().getElement();
+		if (instance)
+		{
+			processElement(instance);
+		}
+	}
+
+	//process children
+	daeTArray< daeSmartRef<daeElement> > children = element->getChildren();
+	for (S32 i = 0; i < children.getCount(); i++)
+	{
+		processElement(children[i]);
+	}
+
+	domNode* node = daeSafeCast<domNode>(element);
+	if (node)
+	{ //this element was a node, restore transform before processiing siblings
+		mTransform = saved_transform;
+	}
+}
+
+std::vector<LLImportMaterial> LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo)
+{
+	std::vector<LLImportMaterial> materials;
+	for (int i = 0; i < model->mMaterialList.size(); i++)
+	{
+		LLImportMaterial import_material;
+
+		domInstance_material* instance_mat = NULL;
+
+		domBind_material::domTechnique_common* technique =
+		daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID())));
+
+		if (technique)
+		{
+			daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>();
+			for (int j = 0; j < inst_materials.getCount(); j++)
+			{
+				std::string symbol(inst_materials[j]->getSymbol());
+
+				if (symbol == model->mMaterialList[i]) // found the binding
+				{
+					instance_mat = inst_materials[j];
+				}
+			}
+		}
+
+		if (instance_mat)
+		{
+			domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement());
+			if (material)
+			{
+				domInstance_effect* instance_effect =
+				daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID())));
+				if (instance_effect)
+				{
+					domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement());
+					if (effect)
+					{
+						domProfile_COMMON* profile =
+						daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID())));
+						if (profile)
+						{
+							import_material = profileToMaterial(profile);
+						}
+					}
+				}
+			}
+		}
+
+		materials.push_back(import_material);
+	}
+
+	return materials;
+}
+
+LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material)
+{
+	LLImportMaterial mat;
+	mat.mFullbright = FALSE;
+
+	daeElement* diffuse = material->getDescendant("diffuse");
+	if (diffuse)
+	{
+		domCommon_color_or_texture_type_complexType::domTexture* texture =
+		daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture"));
+		if (texture)
+		{
+			domCommon_newparam_type_Array newparams = material->getNewparam_array();
+			for (S32 i = 0; i < newparams.getCount(); i++)
+			{
+				domFx_surface_common* surface = newparams[i]->getSurface();
+				if (surface)
+				{
+					domFx_surface_init_common* init = surface->getFx_surface_init_common();
+					if (init)
+					{
+						domFx_surface_init_from_common_Array init_from = init->getInit_from_array();
+
+						if (init_from.getCount() > i)
+						{
+							domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement());
+							if (image)
+							{
+								// we only support init_from now - embedded data will come later
+								domImage::domInit_from* init = image->getInit_from();
+								if (init)
+								{									
+									mat.mDiffuseMapFilename = cdom::uriToNativePath(init->getValue().str());
+									mat.mDiffuseMapLabel = getElementLabel(material);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		domCommon_color_or_texture_type_complexType::domColor* color =
+		daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color"));
+		if (color)
+		{
+			domFx_color_common domfx_color = color->getValue();
+			LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+			mat.mDiffuseColor = value;
+		}
+	}
+
+	daeElement* emission = material->getDescendant("emission");
+	if (emission)
+	{
+		LLColor4 emission_color = getDaeColor(emission);
+		if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25)
+		{
+			mat.mFullbright = TRUE;
+		}
+	}
+
+	return mat;
+}
+
+// try to get a decent label for this element
+std::string LLModelLoader::getElementLabel(daeElement *element)
+{
+	// if we have a name attribute, use it
+	std::string name = element->getAttribute("name");
+	if (name.length())
+	{
+		return name;
+	}
+
+	// if we have an ID attribute, use it
+	if (element->getID())
+	{
+		return std::string(element->getID());
+	}
+
+	// if we have a parent, use it
+	daeElement* parent = element->getParent();
+	if (parent)
+	{
+		// if parent has a name, use it
+		std::string name = parent->getAttribute("name");
+		if (name.length())
+		{
+			return name;
+		}
+
+		// if parent has an ID, use it
+		if (parent->getID())
+		{
+			return std::string(parent->getID());
+		}
+	}
+
+	// try to use our type
+	daeString element_name = element->getElementName();
+	if (element_name)
+	{
+		return std::string(element_name);
+	}
+
+	// if all else fails, use "object"
+	return std::string("object");
+}
+
+LLColor4 LLModelLoader::getDaeColor(daeElement* element)
+{
+	LLColor4 value;
+	domCommon_color_or_texture_type_complexType::domColor* color =
+	daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color"));
+	if (color)
+	{
+		domFx_color_common domfx_color = color->getValue();
+		value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+	}
+
+	return value;
+}
+
+//-----------------------------------------------------------------------------
+// LLModelPreview
+//-----------------------------------------------------------------------------
+
+LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
+: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL)
+, mPelvisZOffset( 0.0f )
+, mLegacyRigValid( false )
+, mRigValidJointUpload( false )
+, mResetJoints( false )
+, mRigParityWithScene( false )
+, mLastJointUpdate( false )
+{
+	mNeedsUpdate = TRUE;
+	mCameraDistance = 0.f;
+	mCameraYaw = 0.f;
+	mCameraPitch = 0.f;
+	mCameraZoom = 1.f;
+	mTextureName = 0;
+	mPreviewLOD = 0;
+	mModelLoader = NULL;
+	mMaxTriangleLimit = 0;
+	mDirty = false;
+	mGenLOD = false;
+	mLoading = false;
+	mLoadState = LLModelLoader::STARTING;
+	mGroup = 0;
+	mBuildShareTolerance = 0.f;
+	mBuildQueueMode = GLOD_QUEUE_GREEDY;
+	mBuildBorderMode = GLOD_BORDER_UNLOCK;
+	mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE;
+
+	for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
+	{
+		mRequestedTriangleCount[i] = 0;
+	}
+
+	mViewOption["show_textures"] = false;
+
+	mFMP = fmp;
+
+	mHasPivot = false;
+	mModelPivot = LLVector3( 0.0f, 0.0f, 0.0f );
+	
+	glodInit();
+
+	//move into joint mapper class
+	//1. joints for joint offset verification
+	mMasterJointList.push_front("mPelvis");
+	mMasterJointList.push_front("mTorso");
+	mMasterJointList.push_front("mChest");
+	mMasterJointList.push_front("mNeck");
+	mMasterJointList.push_front("mHead");
+	mMasterJointList.push_front("mCollarLeft");
+	mMasterJointList.push_front("mShoulderLeft");
+	mMasterJointList.push_front("mElbowLeft");
+	mMasterJointList.push_front("mWristLeft");
+	mMasterJointList.push_front("mCollarRight");
+	mMasterJointList.push_front("mShoulderRight");
+	mMasterJointList.push_front("mElbowRight");
+	mMasterJointList.push_front("mWristRight");
+	mMasterJointList.push_front("mHipRight");
+	mMasterJointList.push_front("mKneeRight");
+	mMasterJointList.push_front("mFootRight");
+	mMasterJointList.push_front("mHipLeft");
+	mMasterJointList.push_front("mKneeLeft");
+	mMasterJointList.push_front("mFootLeft");
+	//2. legacy joint list - used to verify rigs that will not be using joint offsets
+	mMasterLegacyJointList.push_front("mPelvis");
+	mMasterLegacyJointList.push_front("mTorso");
+	mMasterLegacyJointList.push_front("mChest");
+	mMasterLegacyJointList.push_front("mNeck");
+	mMasterLegacyJointList.push_front("mHead");
+	mMasterLegacyJointList.push_front("mHipRight");
+	mMasterLegacyJointList.push_front("mKneeRight");
+	mMasterLegacyJointList.push_front("mFootRight");
+	mMasterLegacyJointList.push_front("mHipLeft");
+	mMasterLegacyJointList.push_front("mKneeLeft");
+	mMasterLegacyJointList.push_front("mFootLeft");
+}
+
+LLModelPreview::~LLModelPreview()
+{
+	if (mModelLoader)
+	{
+		delete mModelLoader;
+		mModelLoader = NULL;
+	}
+	//*HACK : *TODO : turn this back on when we understand why this crashes
+	//glodShutdown();
+}
+
+U32 LLModelPreview::calcResourceCost()
+{
+	assert_main_thread();
+
+	rebuildUploadData();
+
+	if (mFMP && mModelLoader)
+	{
+		if ( getLoadState() < LLModelLoader::ERROR_PARSING )
+		{
+			mFMP->childEnable("ok_btn");
+		}
+	}
+
+	//Upload skin is selected BUT check to see if the joints coming in from the asset were malformed.
+	if ( mFMP && mFMP->childGetValue("upload_skin").asBoolean() )
+	{
+		bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
+		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
+		{
+			mFMP->childDisable("ok_btn");		
+		}
+		else
+		if ( !isLegacyRigValid() )
+		{
+			mFMP->childDisable("ok_btn");
+		}
+		//ok_btn should not have been changed unless something was wrong with joint list
+	}
+	
+	U32 cost = 0;
+	std::set<LLModel*> accounted;
+	U32 num_points = 0;
+	U32 num_hulls = 0;
+
+	F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f;
+	mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f;
+	
+	if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() )
+	{
+		gAgentAvatarp->setPelvisOffset( mPelvisZOffset );
+	}
+
+	F32 streaming_cost = 0.f;
+	F32 physics_cost = 0.f;
+	for (U32 i = 0; i < mUploadData.size(); ++i)
+	{
+		LLModelInstance& instance = mUploadData[i];
+		
+		if (accounted.find(instance.mModel) == accounted.end())
+		{
+			accounted.insert(instance.mModel);
+
+			LLModel::Decomposition& decomp =
+			instance.mLOD[LLModel::LOD_PHYSICS] ?
+			instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics :
+			instance.mModel->mPhysics;
+			
+			//update instance skin info for each lods pelvisZoffset 
+			for ( int j=0; j<LLModel::NUM_LODS; ++j )
+			{	
+				if ( instance.mLOD[j] )
+				{
+					instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset;
+				}
+			}
+
+			std::stringstream ostr;
+			LLSD ret = LLModel::writeModel(ostr,
+					   instance.mLOD[4],
+					   instance.mLOD[3],
+					   instance.mLOD[2],
+					   instance.mLOD[1],
+					   instance.mLOD[0],
+					   decomp,
+					   mFMP->childGetValue("upload_skin").asBoolean(),
+					   mFMP->childGetValue("upload_joints").asBoolean(),
+					   TRUE);
+			cost += gMeshRepo.calcResourceCost(ret);
+
+			num_hulls += decomp.mHull.size();
+			for (U32 i = 0; i < decomp.mHull.size(); ++i)
+			{
+				num_points += decomp.mHull[i].size();
+			}
+
+			//calculate streaming cost
+			LLMatrix4 transformation = instance.mTransform;
+
+			LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+			LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+			LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+			LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+			F32 x_length = x_transformed.normalize();
+			F32 y_length = y_transformed.normalize();
+			F32 z_length = z_transformed.normalize();
+			LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+			F32 radius = scale.length()*debug_scale;
+
+			streaming_cost += LLMeshRepository::getStreamingCost(ret, radius);
+		}
+	}
+
+	F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f;
+
+	mDetailsSignal(mPreviewScale[0]*scale, mPreviewScale[1]*scale, mPreviewScale[2]*scale, streaming_cost, physics_cost);
+
+	updateStatusMessages();
+
+	return cost;
+}
+
+void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)
+{
+	childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x));
+	childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y));
+	childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z));
+	childSetTextArg("streaming cost", "[COST]", llformat("%.3f", streaming_cost));
+	childSetTextArg("physics cost", "[COST]", llformat("%.3f", physics_cost));	
+}
+
+
+void LLModelPreview::rebuildUploadData()
+{
+	assert_main_thread();
+
+	mUploadData.clear();
+	mTextureSet.clear();
+
+	//fill uploaddata instance vectors from scene data
+
+	std::string requested_name = mFMP->getChild<LLUICtrl>("description_form")->getValue().asString();
+
+
+	LLSpinCtrl* scale_spinner = mFMP->getChild<LLSpinCtrl>("import_scale");
+
+	if (!scale_spinner)
+	{
+		llerrs << "floater_model_preview.xml MUST contain import_scale spinner." << llendl;
+	}
+
+	F32 scale = scale_spinner->getValue().asReal();
+
+	LLMatrix4 scale_mat;
+	scale_mat.initScale(LLVector3(scale, scale, scale));
+
+	F32 max_scale = 0.f;
+
+	if ( mBaseScene.size() > 0 )
+	{
+		mFMP->childEnable("ok_btn");
+	}
+
+	for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
+	{ //for each transform in scene
+		LLMatrix4 mat = iter->first;
+
+		// compute position
+		LLVector3 position = LLVector3(0, 0, 0) * mat;
+
+		// compute scale
+		LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position;
+		LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position;
+		LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position;
+		F32 x_length = x_transformed.normalize();
+		F32 y_length = y_transformed.normalize();
+		F32 z_length = z_transformed.normalize();
+
+		max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length);
+
+		mat *= scale_mat;
+
+		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+		{ //for each instance with said transform applied
+			LLModelInstance instance = *model_iter;
+
+			LLModel* base_model = instance.mModel;
+			
+			if (base_model)
+			{
+				base_model->mRequestedLabel = requested_name;
+			}
+
+			S32 idx = 0;
+			for (idx = 0; idx < mBaseModel.size(); ++idx)
+			{  //find reference instance for this model
+				if (mBaseModel[idx] == base_model)
+				{
+					break;
+				}
+			}
+
+			for (U32 i = 0; i < LLModel::NUM_LODS; i++)
+			{ //fill LOD slots based on reference model index
+				if (!mModel[i].empty())
+				{
+					instance.mLOD[i] = mModel[i][idx];
+				}
+				else
+				{
+					instance.mLOD[i] = NULL;
+				}
+			}
+
+			instance.mTransform = mat;
+			mUploadData.push_back(instance);
+		}
+	}
+
+	F32 max_import_scale = DEFAULT_MAX_PRIM_SCALE/max_scale;
+
+	scale_spinner->setMaxValue(max_import_scale);
+
+	if (max_import_scale < scale)
+	{
+		scale_spinner->setValue(max_import_scale);
+	}
+
+}
+
+void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions)
+{
+	if (!mLODFile[LLModel::LOD_HIGH].empty())
+	{
+		std::string filename = mLODFile[LLModel::LOD_HIGH];
+		
+		std::string::size_type i = filename.rfind(".");
+		if (i != std::string::npos)
+		{
+			filename.replace(i, filename.size()-1, ".slm");
+			saveUploadData(filename, save_skinweights, save_joint_positions);
+		}
+	}
+}
+
+void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions)
+{
+	if (!gSavedSettings.getBOOL("MeshImportUseSLM"))
+	{
+		return;
+	}
+
+	std::set<LLPointer<LLModel> > meshes;
+	std::map<LLModel*, std::string> mesh_binary;
+
+	LLModel::hull empty_hull;
+
+	LLSD data;
+
+	S32 mesh_id = 0;
+
+	//build list of unique models and initialize local id
+	for (U32 i = 0; i < mUploadData.size(); ++i)
+	{
+		LLModelInstance& instance = mUploadData[i];
+		
+		if (meshes.find(instance.mModel) == meshes.end())
+		{
+			instance.mModel->mLocalID = mesh_id++;
+			meshes.insert(instance.mModel);
+
+			std::stringstream str;
+
+			LLModel::Decomposition& decomp =
+				instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? 
+				instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : 
+				instance.mModel->mPhysics;
+
+			LLModel::writeModel(str, 
+				instance.mLOD[LLModel::LOD_PHYSICS], 
+				instance.mLOD[LLModel::LOD_HIGH], 
+				instance.mLOD[LLModel::LOD_MEDIUM], 
+				instance.mLOD[LLModel::LOD_LOW], 
+				instance.mLOD[LLModel::LOD_IMPOSTOR], 
+				decomp, 
+				save_skinweights, save_joint_positions);
+
+			
+			data["mesh"][instance.mModel->mLocalID] = str.str();
+		}
+
+		data["instance"][i] = instance.asLLSD();
+	}
+
+	llofstream out(filename, std::ios_base::out | std::ios_base::binary);
+	LLSDSerialize::toBinary(data, out);
+	out.flush();
+	out.close();
+}
+
+void LLModelPreview::clearModel(S32 lod)
+{
+	if (lod < 0 || lod > LLModel::LOD_PHYSICS)
+	{
+		return;
+	}
+
+	mVertexBuffer[lod].clear();
+	mModel[lod].clear();
+	mScene[lod].clear();
+}
+
+void LLModelPreview::loadModel(std::string filename, S32 lod)
+{
+	assert_main_thread();
+
+	LLMutexLock lock(this);
+
+	// This triggers if you bring up the file picker and then hit CANCEL.
+	// Just use the previous model (if any) and ignore that you brought up
+	// the file picker.
+
+	if (filename.empty())
+	{
+		if (mBaseModel.empty())
+		{
+			// this is the initial file picking. Close the whole floater
+			// if we don't have a base model to show for high LOD.
+			mFMP->closeFloater(false);
+			mLoading = false;
+		}
+		return;
+	}
+
+	if (mModelLoader)
+	{
+		llwarns << "Incompleted model load operation pending." << llendl;
+		return;
+	}
+	
+	mLODFile[lod] = filename;
+
+	if (lod == LLModel::LOD_HIGH)
+	{
+		clearGLODGroup();
+	}
+
+	mModelLoader = new LLModelLoader(filename, lod, this, mJointTransformMap, mJointsFromNode );
+
+	mModelLoader->start();
+
+	mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file"));
+
+	setPreviewLOD(lod);
+
+	if ( getLoadState() >= LLModelLoader::ERROR_PARSING )
+	{
+		mFMP->childDisable("ok_btn");
+	}
+	
+	if (lod == mPreviewLOD)
+	{
+		mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]);
+	}
+	else if (lod == LLModel::LOD_PHYSICS)
+	{
+		mFMP->childSetText("physics_file", mLODFile[lod]);
+	}
+
+	mFMP->openFloater();
+}
+
+void LLModelPreview::setPhysicsFromLOD(S32 lod)
+{
+	assert_main_thread();
+
+	if (lod >= 0 && lod <= 3)
+	{
+		mModel[LLModel::LOD_PHYSICS] = mModel[lod];
+		mScene[LLModel::LOD_PHYSICS] = mScene[lod];
+		mLODFile[LLModel::LOD_PHYSICS].clear();
+		mFMP->childSetText("physics_file", mLODFile[LLModel::LOD_PHYSICS]);
+		mVertexBuffer[LLModel::LOD_PHYSICS].clear();
+		rebuildUploadData();
+		refresh();
+		updateStatusMessages();
+	}
+}
+
+void LLModelPreview::clearIncompatible(S32 lod)
+{
+	for (U32 i = 0; i <= LLModel::LOD_HIGH; i++)
+	{ //clear out any entries that aren't compatible with this model
+		if (i != lod)
+		{
+			if (mModel[i].size() != mModel[lod].size())
+			{
+				mModel[i].clear();
+				mScene[i].clear();
+				mVertexBuffer[i].clear();
+
+				if (i == LLModel::LOD_HIGH)
+				{
+					mBaseModel = mModel[lod];
+					clearGLODGroup();
+					mBaseScene = mScene[lod];
+					mVertexBuffer[5].clear();
+				}
+			}
+		}
+	}
+}
+
+void LLModelPreview::clearGLODGroup()
+{
+	if (mGroup)
+	{
+		for (std::map<LLPointer<LLModel>, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter)
+		{
+			glodDeleteObject(iter->second);
+			stop_gloderror();
+		}
+		mObject.clear();
+
+		glodDeleteGroup(mGroup);
+		stop_gloderror();
+		mGroup = 0;
+	}
+}
+
+void LLModelPreview::loadModelCallback(S32 lod)
+{
+	assert_main_thread();
+
+	LLMutexLock lock(this);
+	if (!mModelLoader)
+	{
+		mLoading = false ;
+		return;
+	}
+	if(getLoadState() >= LLModelLoader::ERROR_PARSING)
+	{
+		mLoading = false ;
+		return ;
+	}
+
+	mModelLoader->loadTextures() ;
+
+	if (lod == -1)
+	{ //populate all LoDs from model loader scene
+		mBaseModel.clear();
+		mBaseScene.clear();
+
+		bool skin_weights = false;
+		bool joint_positions = false;
+
+		for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+		{ //for each LoD
+
+			//clear scene and model info
+			mScene[lod].clear();
+			mModel[lod].clear();
+			mVertexBuffer[lod].clear();
+			
+			if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull())
+			{ //if this LoD exists in the loaded scene
+
+				//copy scene to current LoD
+				mScene[lod] = mModelLoader->mScene;
+			
+				//touch up copied scene to look like current LoD
+				for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
+				{
+					LLModelLoader::model_instance_list& list = iter->second;
+
+					for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter)
+					{	
+						//override displayed model with current LoD
+						list_iter->mModel = list_iter->mLOD[lod];
+
+						//add current model to current LoD's model list (LLModel::mLocalID makes a good vector index)
+						S32 idx = list_iter->mModel->mLocalID;
+
+						if (mModel[lod].size() <= idx)
+						{ //stretch model list to fit model at given index
+							mModel[lod].resize(idx+1);
+						}
+
+						mModel[lod][idx] = list_iter->mModel;	
+						if (!list_iter->mModel->mSkinWeights.empty())
+						{
+							skin_weights = true;
+
+							if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty())
+							{
+								joint_positions = true;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		if (mFMP)
+		{
+			LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP;
+
+			if (skin_weights)
+			{ //enable uploading/previewing of skin weights if present in .slm file
+				fmp->enableViewOption("show_skin_weight");
+				mViewOption["show_skin_weight"] = true;
+				fmp->childSetValue("upload_skin", true);
+			}
+
+			if (joint_positions)
+			{ 
+				fmp->enableViewOption("show_joint_positions");
+				mViewOption["show_joint_positions"] = true;
+				fmp->childSetValue("upload_joints", true);
+			}
+		}
+
+		//copy high lod to base scene for LoD generation
+		mBaseScene = mScene[LLModel::LOD_HIGH];
+		mBaseModel = mModel[LLModel::LOD_HIGH];
+
+		mDirty = true;
+		resetPreviewTarget();
+	}
+	else
+	{ //only replace given LoD
+		mModel[lod] = mModelLoader->mModelList;
+		mScene[lod] = mModelLoader->mScene;
+		mVertexBuffer[lod].clear();
+
+		setPreviewLOD(lod);
+
+		if (lod == LLModel::LOD_HIGH)
+		{ //save a copy of the highest LOD for automatic LOD manipulation
+			if (mBaseModel.empty())
+			{ //first time we've loaded a model, auto-gen LoD
+				mGenLOD = true;
+			}
+
+			mBaseModel = mModel[lod];
+			clearGLODGroup();
+
+			mBaseScene = mScene[lod];
+			mVertexBuffer[5].clear();
+		}
+
+		clearIncompatible(lod);
+
+		mDirty = true;
+
+		if (lod == LLModel::LOD_HIGH)
+		{
+			resetPreviewTarget();
+		}
+	}
+
+	mLoading = false;
+	refresh();
+
+	mModelLoadedSignal();
+}
+
+void LLModelPreview::resetPreviewTarget()
+{
+	if ( mModelLoader )
+	{
+		mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f;
+		mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f;
+	}
+
+	setPreviewTarget(mPreviewScale.magVec()*2.f);
+}
+
+void LLModelPreview::generateNormals()
+{
+	assert_main_thread();
+
+	S32 which_lod = mPreviewLOD;
+
+
+	if (which_lod > 4 || which_lod < 0 ||
+		mModel[which_lod].empty())
+	{
+		return;
+	}
+
+	F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal();
+
+	angle_cutoff *= DEG_TO_RAD;
+
+	if (which_lod == 3 && !mBaseModel.empty())
+	{
+		for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+		{
+			(*iter)->generateNormals(angle_cutoff);
+		}
+
+		mVertexBuffer[5].clear();
+	}
+
+	for (LLModelLoader::model_list::iterator iter = mModel[which_lod].begin(); iter != mModel[which_lod].end(); ++iter)
+	{
+		(*iter)->generateNormals(angle_cutoff);
+	}
+
+	mVertexBuffer[which_lod].clear();
+	refresh();
+
+}
+
+void LLModelPreview::clearMaterials()
+{
+	for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+	{ //for each transform in current scene
+		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+		{ //for each instance with that transform
+			LLModelInstance& source_instance = *model_iter;
+			LLModel* source = source_instance.mModel;
+
+			for (S32 i = 0; i < source->getNumVolumeFaces(); ++i)
+			{ //for each face in instance
+				LLImportMaterial& source_material = source_instance.mMaterial[i];
+
+				//clear material info
+				source_material.mDiffuseColor = LLColor4(1,1,1,1);
+				source_material.mDiffuseMap = NULL;
+				source_material.mDiffuseMapFilename.clear();
+				source_material.mDiffuseMapLabel.clear();
+				source_material.mFullbright = false;
+			}
+		}
+	}
+
+	mVertexBuffer[mPreviewLOD].clear();
+
+	if (mPreviewLOD == LLModel::LOD_HIGH)
+	{
+		mBaseScene = mScene[mPreviewLOD];
+		mBaseModel = mModel[mPreviewLOD];
+		clearGLODGroup();
+		mVertexBuffer[5].clear();
+	}
+
+	mResourceCost = calcResourceCost();
+	refresh();
+}
+
+void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit)
+{
+	if (mBaseModel.empty())
+	{
+		return;
+	}
+
+	LLVertexBuffer::unbind();
+
+	stop_gloderror();
+	static U32 cur_name = 1;
+
+	S32 limit = -1;
+
+	U32 triangle_count = 0;
+
+	for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+	{
+		LLModel* mdl = *iter;
+		for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+		{
+			triangle_count += mdl->getVolumeFace(i).mNumIndices/3;
+		}
+	}
+
+	U32 base_triangle_count = triangle_count;
+
+	U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+	U32 lod_mode = 0;
+
+	LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode");
+	if (iface)
+	{
+		lod_mode = iface->getFirstSelectedIndex();
+	}
+
+	F32 lod_error_threshold = mFMP->childGetValue("lod_error_threshold").asReal();
+
+	if (lod_mode == 0)
+	{
+		lod_mode = GLOD_TRIANGLE_BUDGET;
+		if (which_lod != -1)
+		{
+			limit = mFMP->childGetValue("lod_triangle_limit").asInteger();
+		}
+	}
+	else
+	{
+		lod_mode = GLOD_ERROR_THRESHOLD;
+	}
+
+	U32 build_operator = 0;
+
+	iface = mFMP->childGetSelectionInterface("build_operator");
+	if (iface)
+	{
+		build_operator = iface->getFirstSelectedIndex();
+	}
+
+	if (build_operator == 0)
+	{
+		build_operator = GLOD_OPERATOR_EDGE_COLLAPSE;
+	}
+	else
+	{
+		build_operator = GLOD_OPERATOR_HALF_EDGE_COLLAPSE;
+	}
+
+	U32 queue_mode=0;
+	iface = mFMP->childGetSelectionInterface("queue_mode");
+	if (iface)
+	{
+		queue_mode = iface->getFirstSelectedIndex();
+	}
+
+	if (queue_mode == 0)
+	{
+		queue_mode = GLOD_QUEUE_GREEDY;
+	}
+	else if (queue_mode == 1)
+	{
+		queue_mode = GLOD_QUEUE_LAZY;
+	}
+	else
+	{
+		queue_mode = GLOD_QUEUE_INDEPENDENT;
+	}
+
+	U32 border_mode = 0;
+
+	iface = mFMP->childGetSelectionInterface("border_mode");
+	if (iface)
+	{
+		border_mode = iface->getFirstSelectedIndex();
+	}
+
+	if (border_mode == 0)
+	{
+		border_mode = GLOD_BORDER_UNLOCK;
+	}
+	else
+	{
+		border_mode = GLOD_BORDER_LOCK;
+	}
+
+	bool object_dirty = false;
+	if (border_mode != mBuildBorderMode)
+	{
+		mBuildBorderMode = border_mode;
+		object_dirty = true;
+	}
+
+	if (queue_mode != mBuildQueueMode)
+	{
+		mBuildQueueMode = queue_mode;
+		object_dirty = true;
+	}
+
+	if (build_operator != mBuildOperator)
+	{
+		mBuildOperator = build_operator;
+		object_dirty = true;
+	}
+
+	F32 share_tolerance = mFMP->childGetValue("share_tolerance").asReal();
+	if (share_tolerance != mBuildShareTolerance)
+	{
+		mBuildShareTolerance = share_tolerance;
+		object_dirty = true;
+	}
+
+	if (mGroup == 0)
+	{
+		object_dirty = true;
+		mGroup = cur_name++;
+		glodNewGroup(mGroup);
+	}
+
+	if (object_dirty)
+	{
+		for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+		{ //build GLOD objects for each model in base model list
+			LLModel* mdl = *iter;
+
+			if (mObject[mdl] != 0)
+			{
+				glodDeleteObject(mObject[mdl]);
+			}
+
+			mObject[mdl] = cur_name++;
+
+			glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE);
+			stop_gloderror();
+
+			if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty())
+			{ //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation
+				mVertexBuffer[5].clear();
+			}
+
+			if (mVertexBuffer[5].empty())
+			{
+				genBuffers(5, false);
+			}
+
+			U32 tri_count = 0;
+			for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i)
+			{
+				mVertexBuffer[5][mdl][i]->setBuffer(type_mask);
+				U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices();
+				if (num_indices > 2)
+				{
+					glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f);
+				}
+				tri_count += num_indices/3;
+				stop_gloderror();
+			}
+
+			glodObjectParameteri(mObject[mdl], GLOD_BUILD_OPERATOR, build_operator);
+			stop_gloderror();
+
+			glodObjectParameteri(mObject[mdl], GLOD_BUILD_QUEUE_MODE, queue_mode);
+			stop_gloderror();
+
+			glodObjectParameteri(mObject[mdl], GLOD_BUILD_BORDER_MODE, border_mode);
+			stop_gloderror();
+
+			glodObjectParameterf(mObject[mdl], GLOD_BUILD_SHARE_TOLERANCE, share_tolerance);
+			stop_gloderror();
+
+			glodBuildObject(mObject[mdl]);
+			stop_gloderror();
+		}
+	}
+
+
+	S32 start = LLModel::LOD_HIGH;
+	S32 end = 0;
+
+	if (which_lod != -1)
+	{
+		start = end = which_lod;
+	}
+
+	mMaxTriangleLimit = base_triangle_count;
+
+	for (S32 lod = start; lod >= end; --lod)
+	{
+		if (which_lod == -1)
+		{
+			if (lod < start)
+			{
+				triangle_count /= decimation;
+			}
+		}
+		else
+		{
+			if (enforce_tri_limit)
+			{
+				triangle_count = limit;
+			}
+			else
+			{
+				for (S32 j=LLModel::LOD_HIGH; j>which_lod; --j)
+				{
+					triangle_count /= decimation;
+				}
+			}
+		}
+
+		mModel[lod].clear();
+		mModel[lod].resize(mBaseModel.size());
+		mVertexBuffer[lod].clear();
+
+		U32 actual_tris = 0;
+		U32 actual_verts = 0;
+		U32 submeshes = 0;
+
+		mRequestedTriangleCount[lod] = triangle_count;
+
+		glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode);
+		stop_gloderror();
+
+		glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR);
+		stop_gloderror();
+
+		glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold);
+		stop_gloderror();
+
+		if (lod_mode != GLOD_TRIANGLE_BUDGET)
+		{ 			
+			glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0);
+		}
+		else
+		{
+			//SH-632: always add 1 to desired amount to avoid decimating below desired amount
+			glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count+1);
+		}
+			
+		stop_gloderror();
+		glodAdaptGroup(mGroup);
+		stop_gloderror();		
+
+		for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx)
+		{
+			LLModel* base = mBaseModel[mdl_idx];
+
+			GLint patch_count = 0;
+			glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count);
+			stop_gloderror();
+
+			LLVolumeParams volume_params;
+			volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+			mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f);
+
+			GLint* sizes = new GLint[patch_count*2];
+			glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes);
+			stop_gloderror();
+
+			GLint* names = new GLint[patch_count];
+			glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names);
+			stop_gloderror();
+
+			mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count);
+
+			LLModel* target_model = mModel[lod][mdl_idx];
+
+			for (GLint i = 0; i < patch_count; ++i)
+			{
+				LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
+
+				if (sizes[i*2+1] > 0 && sizes[i*2] > 0)
+				{
+					buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true);
+					buff->setBuffer(type_mask);
+					glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer());
+					stop_gloderror();
+				}
+				else
+				{ //this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0)
+					buff->allocateBuffer(1, 3, true);
+					memset(buff->getMappedData(), 0, buff->getSize());
+					memset(buff->getIndicesPointer(), 0, buff->getIndicesSize());
+				}
+
+				buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0);
+
+				LLStrider<LLVector3> pos;
+				LLStrider<LLVector3> norm;
+				LLStrider<LLVector2> tc;
+				LLStrider<U16> index;
+
+				buff->getVertexStrider(pos);
+				buff->getNormalStrider(norm);
+				buff->getTexCoord0Strider(tc);
+				buff->getIndexStrider(index);
+
+				target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
+				actual_tris += buff->getNumIndices()/3;
+				actual_verts += buff->getNumVerts();
+				++submeshes;
+
+				if (!validate_face(target_model->getVolumeFace(names[i])))
+				{
+					llerrs << "Invalid face generated during LOD generation." << llendl;
+				}
+			}
+
+			//blind copy skin weights and just take closest skin weight to point on
+			//decimated mesh for now (auto-generating LODs with skin weights is still a bit
+			//of an open problem).
+			target_model->mPosition = base->mPosition;
+			target_model->mSkinWeights = base->mSkinWeights;
+			target_model->mSkinInfo = base->mSkinInfo;
+			//copy material list
+			target_model->mMaterialList = base->mMaterialList;
+
+			if (!validate_model(target_model))
+			{
+				llerrs << "Invalid model generated when creating LODs" << llendl;
+			}
+
+			delete [] sizes;
+			delete [] names;
+		}
+
+		//rebuild scene based on mBaseScene
+		mScene[lod].clear();
+		mScene[lod] = mBaseScene;
+
+		for (U32 i = 0; i < mBaseModel.size(); ++i)
+		{
+			LLModel* mdl = mBaseModel[i];
+			LLModel* target = mModel[lod][i];
+			if (target)
+			{
+				for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
+				{
+					for (U32 j = 0; j < iter->second.size(); ++j)
+					{
+						if (iter->second[j].mModel == mdl)
+						{
+							iter->second[j].mModel = target;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	mResourceCost = calcResourceCost();
+
+	/*if (which_lod == -1 && mScene[LLModel::LOD_PHYSICS].empty())
+	 { //build physics scene
+	 mScene[LLModel::LOD_PHYSICS] = mScene[LLModel::LOD_LOW];
+	 mModel[LLModel::LOD_PHYSICS] = mModel[LLModel::LOD_LOW];
+
+	 for (U32 i = 1; i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
+	 {
+	 mPhysicsQ.push(mModel[LLModel::LOD_PHYSICS][i]);
+	 }
+	 }*/
+}
+
+void LLModelPreview::updateStatusMessages()
+{
+	assert_main_thread();
+
+	//triangle/vertex/submesh count for each mesh asset for each lod
+	std::vector<S32> tris[LLModel::NUM_LODS];
+	std::vector<S32> verts[LLModel::NUM_LODS];
+	std::vector<S32> submeshes[LLModel::NUM_LODS];
+
+	//total triangle/vertex/submesh count for each lod
+	S32 total_tris[LLModel::NUM_LODS];
+	S32 total_verts[LLModel::NUM_LODS];
+	S32 total_submeshes[LLModel::NUM_LODS];
+
+	for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+	{
+		//initialize total for this lod to 0
+		total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0;
+
+		for (U32 i = 0; i < mModel[lod].size(); ++i)
+		{ //for each model in the lod
+			S32 cur_tris = 0;
+			S32 cur_verts = 0;
+			S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
+
+			for (S32 j = 0; j < cur_submeshes; ++j)
+			{ //for each submesh (face), add triangles and vertices to current total
+				const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
+				cur_tris += face.mNumIndices/3;
+				cur_verts += face.mNumVertices;
+			}
+
+			//add this model to the lod total
+			total_tris[lod] += cur_tris;
+			total_verts[lod] += cur_verts;
+			total_submeshes[lod] += cur_submeshes;
+
+			//store this model's counts to asset data
+			tris[lod].push_back(cur_tris);
+			verts[lod].push_back(cur_verts);
+			submeshes[lod].push_back(cur_submeshes);
+		}
+	}
+
+	if (mMaxTriangleLimit == 0)
+	{
+		mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH];
+	}
+
+
+	mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH]));
+
+	std::string mesh_status_na = mFMP->getString("mesh_status_na");
+
+	S32 upload_status[LLModel::LOD_HIGH+1];
+
+	bool upload_ok = true;
+
+	for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
+	{
+		upload_status[lod] = 0;
+
+		std::string message = "mesh_status_good";
+
+		if (total_tris[lod] > 0)
+		{
+			mFMP->childSetText(lod_triangles_name[lod], llformat("%d", total_tris[lod]));
+			mFMP->childSetText(lod_vertices_name[lod], llformat("%d", total_verts[lod]));
+		}
+		else
+		{
+			if (lod == LLModel::LOD_HIGH)
+			{
+				upload_status[lod] = 2;
+				message = "mesh_status_missing_lod";
+			}
+			else
+			{
+				for (S32 i = lod-1; i >= 0; --i)
+				{
+					if (total_tris[i] > 0)
+					{
+						upload_status[lod] = 2;
+						message = "mesh_status_missing_lod";
+					}
+				}
+			}
+
+			mFMP->childSetText(lod_triangles_name[lod], mesh_status_na);
+			mFMP->childSetText(lod_vertices_name[lod], mesh_status_na);
+		}
+
+		const U32 lod_high = LLModel::LOD_HIGH;
+
+		if (lod != lod_high)
+		{
+			if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high])
+			{ //number of submeshes is different
+				message = "mesh_status_submesh_mismatch";
+				upload_status[lod] = 2;
+			}
+			else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size())
+			{ //number of meshes is different
+				message = "mesh_status_mesh_mismatch";
+				upload_status[lod] = 2;
+			}
+			else if (!verts[lod].empty())
+			{
+				for (U32 i = 0; i < verts[lod].size(); ++i)
+				{
+					S32 max_verts = i < verts[lod+1].size() ? verts[lod+1][i] : 0;
+
+					if (max_verts > 0)
+					{
+						if (verts[lod][i] > max_verts)
+						{ //too many vertices in this lod
+							message = "mesh_status_too_many_vertices";
+							upload_status[lod] = 2;
+						}
+					}
+				}
+			}
+		}
+
+		LLIconCtrl* icon = mFMP->getChild<LLIconCtrl>(lod_icon_name[lod]);
+		LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]);
+		icon->setVisible(true);
+		icon->setImage(img);
+
+		if (upload_status[lod] >= 2)
+		{
+			upload_ok = false;
+		}
+
+		if (lod == mPreviewLOD)
+		{
+			mFMP->childSetText("lod_status_message_text", mFMP->getString(message));
+			icon = mFMP->getChild<LLIconCtrl>("lod_status_message_icon");
+			icon->setImage(img);
+		}
+	}
+
+	bool errorStateFromLoader = getLoadState() >= LLModelLoader::ERROR_PARSING ? true : false;
+
+	bool skinAndRigOk = true;
+	bool uploadingSkin		     = mFMP->childGetValue("upload_skin").asBoolean();
+	bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
+
+	if ( uploadingSkin )
+	{
+		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
+		{
+			skinAndRigOk = false;
+		}
+		else
+		if ( !isLegacyRigValid() )
+		{
+			skinAndRigOk = false;
+		}
+	}
+	
+	if ( upload_ok && !errorStateFromLoader && skinAndRigOk )
+	{
+		mFMP->childEnable("ok_btn");
+	}
+	
+	//add up physics triangles etc
+	S32 start = 0;
+	S32 end = mModel[LLModel::LOD_PHYSICS].size();
+
+	S32 phys_tris = 0;
+	S32 phys_hulls = 0;
+	S32 phys_points = 0;
+
+	for (S32 i = start; i < end; ++i)
+	{ //add up hulls and points and triangles for selected mesh(es)
+		LLModel* model = mModel[LLModel::LOD_PHYSICS][i];
+		S32 cur_submeshes = model->getNumVolumeFaces();
+
+		LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull;
+
+		if (!decomp.empty())
+		{
+			phys_hulls += decomp.size();
+			for (U32 i = 0; i < decomp.size(); ++i)
+			{
+				phys_points += decomp[i].size();
+			}
+		}
+		else
+		{ //choose physics shape OR decomposition, can't use both
+			for (S32 j = 0; j < cur_submeshes; ++j)
+			{ //for each submesh (face), add triangles and vertices to current total
+				const LLVolumeFace& face = model->getVolumeFace(j);
+				phys_tris += face.mNumIndices/3;
+			}
+		}
+	}
+
+	if (phys_tris > 0)
+	{
+		mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris));
+	}
+	else
+	{
+		mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na);
+	}
+
+	if (phys_hulls > 0)
+	{
+		mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls));
+		mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points));
+	}
+	else
+	{
+		mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na);
+		mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na);
+	}
+
+	LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+	if (fmp)
+	{
+		if (phys_tris > 0 || phys_hulls > 0)
+		{
+			if (!fmp->isViewOptionEnabled("show_physics"))
+			{
+				fmp->enableViewOption("show_physics");
+				mViewOption["show_physics"] = true;
+			}
+		}
+		else
+		{
+			fmp->disableViewOption("show_physics");
+			mViewOption["show_physics"] = false;
+
+		}
+
+		//bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean();
+
+		//fmp->childSetEnabled("physics_optimize", !use_hull);
+
+		bool enable = phys_tris > 0 || phys_hulls > 0;
+		//enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean();
+
+		//enable/disable "analysis" UI
+		LLPanel* panel = fmp->getChild<LLPanel>("physics analysis");
+		LLView* child = panel->getFirstChild();
+		while (child)
+		{
+			child->setEnabled(enable);
+			child = panel->findNextSibling(child);
+		}
+
+		enable = phys_hulls > 0;
+		//enable/disable "simplification" UI
+		panel = fmp->getChild<LLPanel>("physics simplification");
+		child = panel->getFirstChild();
+		while (child)
+		{
+			child->setEnabled(enable);
+			child = panel->findNextSibling(child);
+		}
+	}
+
+	const char* lod_controls[] =
+	{
+		"lod_mode",
+		"lod_triangle_limit",
+		"lod_error_tolerance",
+		"build_operator_text",
+		"queue_mode_text",
+		"border_mode_text",
+		"share_tolerance_text",
+		"build_operator",
+		"queue_mode",
+		"border_mode",
+		"share_tolerance"
+	};
+	const U32 num_lod_controls = sizeof(lod_controls)/sizeof(char*);
+
+	const char* file_controls[] =
+	{
+		"lod_browse",
+		"lod_file"
+	};
+	const U32 num_file_controls = sizeof(file_controls)/sizeof(char*);
+
+	if (fmp)
+	{
+		//enable/disable controls based on radio groups
+		if (mFMP->childGetValue("lod_from_file").asBoolean())
+		{ 
+			fmp->mLODMode[mPreviewLOD] = 0;
+			for (U32 i = 0; i < num_file_controls; ++i)
+			{
+				mFMP->childEnable(file_controls[i]);
+			}
+
+			for (U32 i = 0; i < num_lod_controls; ++i)
+			{
+				mFMP->childDisable(lod_controls[i]);
+			}
+		}
+		else if (mFMP->childGetValue("lod_none").asBoolean())
+		{
+			fmp->mLODMode[mPreviewLOD] = 2;
+			for (U32 i = 0; i < num_file_controls; ++i)
+			{
+				mFMP->childDisable(file_controls[i]);
+			}
+
+			for (U32 i = 0; i < num_lod_controls; ++i)
+			{
+				mFMP->childDisable(lod_controls[i]);
+			}
+
+			if (!mModel[mPreviewLOD].empty())
+			{
+				mModel[mPreviewLOD].clear();
+				mScene[mPreviewLOD].clear();
+				mVertexBuffer[mPreviewLOD].clear();
+
+				//this can cause phasing issues with the UI, so reenter this function and return
+				updateStatusMessages();
+				return;
+			}
+		}
+		else
+		{	// auto generate, also the default case for wizard which has no radio selection
+			fmp->mLODMode[mPreviewLOD] = 1;
+
+			for (U32 i = 0; i < num_file_controls; ++i)
+			{
+				mFMP->childDisable(file_controls[i]);
+			}
+
+			for (U32 i = 0; i < num_lod_controls; ++i)
+			{
+				mFMP->childEnable(lod_controls[i]);
+			}
+
+			//if (threshold)
+			{	
+				U32 lod_mode = 0;
+				LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode");
+				if (iface)
+				{
+					lod_mode = iface->getFirstSelectedIndex();
+				}
+
+				LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold");
+				LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit");
+
+				limit->setMaxValue(mMaxTriangleLimit);
+				limit->setValue(mRequestedTriangleCount[mPreviewLOD]);
+
+				if (lod_mode == 0)
+				{
+					limit->setVisible(true);
+					threshold->setVisible(false);
+
+					limit->setMaxValue(mMaxTriangleLimit);
+					limit->setIncrement(mMaxTriangleLimit/32);
+				}
+				else
+				{
+					limit->setVisible(false);
+					threshold->setVisible(true);
+				}
+			}
+		}
+	}
+
+	if (mFMP->childGetValue("physics_load_from_file").asBoolean())
+	{
+		mFMP->childDisable("physics_lod_combo");
+		mFMP->childEnable("physics_file");
+		mFMP->childEnable("physics_browse");
+	}
+	else
+	{
+		mFMP->childEnable("physics_lod_combo");
+		mFMP->childDisable("physics_file");
+		mFMP->childDisable("physics_browse");
+	}
+}
+
+void LLModelPreview::setPreviewTarget(F32 distance)
+{
+	mCameraDistance = distance;
+	mCameraZoom = 1.f;
+	mCameraPitch = 0.f;
+	mCameraYaw = 0.f;
+	mCameraOffset.clearVec();
+}
+
+void LLModelPreview::clearBuffers()
+{
+	for (U32 i = 0; i < 6; i++)
+	{
+		mVertexBuffer[i].clear();
+	}
+}
+
+void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
+{
+	U32 tri_count = 0;
+	U32 vertex_count = 0;
+	U32 mesh_count = 0;
+
+	
+	LLModelLoader::model_list* model = NULL;
+
+	if (lod < 0 || lod > 4)
+	{
+		model = &mBaseModel;
+		lod = 5;
+	}
+	else
+	{
+		model = &(mModel[lod]);
+	}
+
+	if (!mVertexBuffer[lod].empty())
+	{
+		mVertexBuffer[lod].clear();
+	}
+
+	mVertexBuffer[lod].clear();
+
+	LLModelLoader::model_list::iterator base_iter = mBaseModel.begin();
+
+	for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter)
+	{
+		LLModel* mdl = *iter;
+		if (!mdl)
+		{
+			continue;
+		}
+
+		LLModel* base_mdl = *base_iter;
+		base_iter++;
+
+		for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+		{
+			const LLVolumeFace &vf = mdl->getVolumeFace(i);
+			U32 num_vertices = vf.mNumVertices;
+			U32 num_indices = vf.mNumIndices;
+
+			if (!num_vertices || ! num_indices)
+			{
+				continue;
+			}
+
+			LLVertexBuffer* vb = NULL;
+
+			bool skinned = include_skin_weights && !mdl->mSkinWeights.empty();
+
+			U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+			if (skinned)
+			{
+				mask |= LLVertexBuffer::MAP_WEIGHT4;
+			}
+
+			vb = new LLVertexBuffer(mask, 0);
+
+			vb->allocateBuffer(num_vertices, num_indices, TRUE);
+
+			LLStrider<LLVector3> vertex_strider;
+			LLStrider<LLVector3> normal_strider;
+			LLStrider<LLVector2> tc_strider;
+			LLStrider<U16> index_strider;
+			LLStrider<LLVector4> weights_strider;
+
+			vb->getVertexStrider(vertex_strider);
+			vb->getNormalStrider(normal_strider);
+			vb->getTexCoord0Strider(tc_strider);
+			vb->getIndexStrider(index_strider);
+
+			if (skinned)
+			{
+				vb->getWeight4Strider(weights_strider);
+			}
+
+			LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32));
+			LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32));
+			LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32));
+
+			if (skinned)
+			{
+				for (U32 i = 0; i < num_vertices; i++)
+				{
+					//find closest weight to vf.mVertices[i].mPosition
+					LLVector3 pos(vf.mPositions[i].getF32ptr());
+
+					const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos);
+
+					LLVector4 w(0,0,0,0);
+					if (weight_list.size() > 4)
+					{
+						llerrs << "WTF?" << llendl;
+					}
+
+					for (U32 i = 0; i < weight_list.size(); ++i)
+					{
+						F32 wght = llmin(weight_list[i].mWeight, 0.999999f);
+						F32 joint = (F32) weight_list[i].mJointIdx;
+						w.mV[i] = joint + wght;
+					}
+
+					*(weights_strider++) = w;
+				}
+			}
+
+			// build indices
+			for (U32 i = 0; i < num_indices; i++)
+			{
+				*(index_strider++) = vf.mIndices[i];
+			}
+
+			mVertexBuffer[lod][mdl].push_back(vb);
+
+			vertex_count += num_vertices;
+			tri_count += num_indices/3;
+			++mesh_count;
+
+		}
+	}
+}
+
+void LLModelPreview::update()
+{
+	if (mDirty)
+	{
+		mDirty = false;
+		mResourceCost = calcResourceCost();
+		refresh();
+		updateStatusMessages();
+	}
+
+	if (mGenLOD)
+	{
+		mGenLOD = false;
+		genLODs();
+		refresh();
+		updateStatusMessages();
+	}
+
+}
+//-----------------------------------------------------------------------------
+// changeAvatarsJointPositions()
+//-----------------------------------------------------------------------------
+void LLModelPreview::changeAvatarsJointPositions( LLModel* pModel )
+{
+	if ( mMasterJointList.empty() )
+	{
+		return;
+	}
+
+	std::vector<std::string> :: const_iterator jointListItBegin = pModel->mSkinInfo.mJointNames.begin();
+	std::vector<std::string> :: const_iterator jointListItEnd = pModel->mSkinInfo.mJointNames.end();
+
+	S32 index = 0;
+	for ( ; jointListItBegin!=jointListItEnd; ++jointListItBegin, ++index )
+	{	
+		std::string elem = *jointListItBegin;
+		//llinfos<<"joint "<<elem<<llendl;
+
+		S32 matrixCnt = pModel->mSkinInfo.mAlternateBindMatrix.size();
+		if ( matrixCnt < 1 )
+		{
+			llinfos<<"Total WTF moment :"<<matrixCnt<<llendl;
+		}
+		else
+		{
+			LLMatrix4 jointTransform = pModel->mSkinInfo.mAlternateBindMatrix[index];
+
+			LLJoint* pJoint = gAgentAvatarp->getJoint( elem );
+			if ( pJoint )
+			{   
+				pJoint->storeCurrentXform( jointTransform.getTranslation() );												
+			}	
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+// getTranslationForJointOffset()
+//-----------------------------------------------------------------------------
+LLVector3 LLModelPreview::getTranslationForJointOffset( std::string joint )
+{
+	LLMatrix4 jointTransform;
+	if ( mJointTransformMap.find( joint ) != mJointTransformMap.end() )
+	{
+		jointTransform = mJointTransformMap[joint];
+		return jointTransform.getTranslation();
+	}
+	return LLVector3(0.0f,0.0f,0.0f);								
+}
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLModelPreview::render()
+{
+	assert_main_thread();
+
+	LLMutexLock lock(this);
+	mNeedsUpdate = FALSE;
+
+	bool edges = mViewOption["show_edges"];
+	bool joint_positions = mViewOption["show_joint_positions"];
+	bool skin_weight = mViewOption["show_skin_weight"];
+	bool textures = mViewOption["show_textures"];
+	bool physics = mViewOption["show_physics"];
+
+	S32 width = getWidth();
+	S32 height = getHeight();
+
+	LLGLSUIDefault def;
+	LLGLDisable no_blend(GL_BLEND);
+	LLGLEnable cull(GL_CULL_FACE);
+	LLGLDepthTest depth(GL_TRUE);
+	LLGLDisable fog(GL_FOG);
+
+	{
+		//clear background to blue
+		glMatrixMode(GL_PROJECTION);
+		gGL.pushMatrix();
+		glLoadIdentity();
+		glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f);
+
+		glMatrixMode(GL_MODELVIEW);
+		gGL.pushMatrix();
+		glLoadIdentity();
+
+		gGL.color4f(0.169f, 0.169f, 0.169f, 1.f);
+
+		gl_rect_2d_simple( width, height );
+
+		glMatrixMode(GL_PROJECTION);
+		gGL.popMatrix();
+
+		glMatrixMode(GL_MODELVIEW);
+		gGL.popMatrix();
+	}
+
+	LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+	
+	bool has_skin_weights = false;
+	bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean();	
+	bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean();
+
+	bool resetJoints = false;
+	if ( upload_joints != mLastJointUpdate )
+	{
+		if ( mLastJointUpdate )
+		{
+			resetJoints = true;
+		}
+
+		mLastJointUpdate = upload_joints;
+
+	}
+
+	for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+	{
+		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+		{
+			LLModelInstance& instance = *model_iter;
+			LLModel* model = instance.mModel;
+			model->mPelvisOffset = mPelvisZOffset;
+			if (!model->mSkinWeights.empty())
+			{
+				has_skin_weights = true;
+			}
+		}
+	}
+
+	if (has_skin_weights)
+	{ //model has skin weights, enable view options for skin weights and joint positions
+		if (fmp)
+		{
+			fmp->enableViewOption("show_skin_weight");
+			fmp->setViewOptionEnabled("show_joint_positions", skin_weight);	
+		}
+		mFMP->childEnable("upload_skin");
+	}
+	else
+	{
+		mFMP->childDisable("upload_skin");
+		if (fmp)
+		{
+			mViewOption["show_skin_weight"] = false;
+			fmp->disableViewOption("show_skin_weight");
+			fmp->disableViewOption("show_joint_positions");
+		}
+		skin_weight = false;
+	}
+
+	if (upload_skin && !has_skin_weights)
+	{ //can't upload skin weights if model has no skin weights
+		mFMP->childSetValue("upload_skin", false);
+		upload_skin = false;
+	}
+
+	if (!upload_skin && upload_joints)
+	{ //can't upload joints if not uploading skin weights
+		mFMP->childSetValue("upload_joints", false);
+		upload_joints = false;		
+	}	
+	
+	mFMP->childSetEnabled("upload_joints", upload_skin);
+
+	//poke at avatar when we upload custom joints
+	/*	
+	if ( upload_joints )
+	{
+		for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+		{
+			for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+			{
+				LLModelInstance& instance = *model_iter;
+				LLModel* model = instance.mModel;
+				if ( !model->mSkinWeights.empty() )
+				{
+					changeAvatarsJointPositions( model );
+				}
+			}
+		}
+	}
+	*/
+	
+	F32 explode = mFMP->childGetValue("physics_explode").asReal();
+
+	glClear(GL_DEPTH_BUFFER_BIT);
+
+	LLRect preview_rect = mFMP->getChildView("preview_panel")->getRect();
+	F32 aspect = (F32) preview_rect.getWidth()/preview_rect.getHeight();
+
+	LLViewerCamera::getInstance()->setAspect(aspect);
+
+	LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom);
+
+	LLVector3 offset = mCameraOffset;
+	LLVector3 target_pos = mPreviewTarget+offset;
+
+	F32 z_near = 0.001f;
+	F32 z_far = mCameraDistance+mPreviewScale.magVec()+mCameraOffset.magVec();
+
+	if (skin_weight)
+	{
+		target_pos = gAgentAvatarp->getPositionAgent();
+		z_near = 0.01f;
+		z_far = 1024.f;
+		mCameraDistance = 16.f;
+
+		//render avatar previews every frame
+		refresh();
+	}
+
+	glLoadIdentity();
+	gPipeline.enableLightsPreview();
+
+	LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+	LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+	LLQuaternion av_rot = camera_rot;
+	LLViewerCamera::getInstance()->setOriginAndLookAt(
+													  target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot),		// camera
+													  LLVector3::z_axis,																	// up
+													  target_pos);											// point of interest
+
+
+	LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far);
+
+	stop_glerror();
+
+	gGL.pushMatrix();
+	const F32 BRIGHTNESS = 0.9f;
+	gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
+
+	LLGLEnable normalize(GL_NORMALIZE);
+
+	if (!mBaseModel.empty() && mVertexBuffer[5].empty())
+	{
+		genBuffers(-1, skin_weight);
+		//genBuffers(3);
+		//genLODs();
+	}
+
+	if (!mModel[mPreviewLOD].empty())
+	{
+		bool regen = mVertexBuffer[mPreviewLOD].empty();
+		if (!regen)
+		{
+			const std::vector<LLPointer<LLVertexBuffer> >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second;
+			if (!vb_vec.empty())
+			{
+				const LLVertexBuffer* buff = vb_vec[0];
+				regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight;
+			}
+		}
+
+		if (regen)
+		{
+			genBuffers(mPreviewLOD, skin_weight);
+		}
+
+		if (!skin_weight)
+		{
+			for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+			{
+				LLModelInstance& instance = *iter;
+
+				LLModel* model = instance.mLOD[mPreviewLOD];
+
+				if (!model)
+				{
+					continue;
+				}
+
+				gGL.pushMatrix();
+				LLMatrix4 mat = instance.mTransform;
+
+				glMultMatrixf((GLfloat*) mat.mMatrix);
+
+				for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
+				{
+					LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+
+					buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+
+					if (textures)
+					{
+						glColor4fv(instance.mMaterial[i].mDiffuseColor.mV);
+						if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull())
+						{
+							if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1)
+							{
+								gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true);
+								mTextureSet.insert(instance.mMaterial[i].mDiffuseMap.get());
+							}
+						}
+					}
+					else
+					{
+						glColor4f(1,1,1,1);
+					}
+
+					buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+					gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+					glColor3f(0.4f, 0.4f, 0.4f);
+
+					if (edges)
+					{
+						glLineWidth(3.f);
+						glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+						buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+						glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+						glLineWidth(1.f);
+					}
+				}
+				gGL.popMatrix();
+			}
+
+			if (physics)
+			{
+				glClear(GL_DEPTH_BUFFER_BIT);
+				LLGLEnable blend(GL_BLEND);
+				gGL.blendFunc(LLRender::BF_ONE, LLRender::BF_ZERO);
+
+				for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+				{
+					LLModelInstance& instance = *iter;
+
+					LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
+
+					if (!model)
+					{
+						continue;
+					}
+
+					gGL.pushMatrix();
+					LLMatrix4 mat = instance.mTransform;
+
+					glMultMatrixf((GLfloat*) mat.mMatrix);
+
+
+					bool render_mesh = true;
+
+					LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+					if (decomp)
+					{
+						LLMutexLock(decomp->mMutex);
+
+						LLModel::Decomposition& physics = model->mPhysics;
+
+						if (physics.mMesh.empty())
+						{ //build vertex buffer for physics mesh
+							gMeshRepo.buildPhysicsMesh(physics);
+						}
+							
+						if (!physics.mMesh.empty())
+						{ //render hull instead of mesh
+							render_mesh = false;
+							for (U32 i = 0; i < physics.mMesh.size(); ++i)
+							{
+								if (explode > 0.f)
+								{
+									gGL.pushMatrix();
+
+									LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters;
+									offset *= explode;
+
+									gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
+								}
+
+								static std::vector<LLColor4U> hull_colors;
+
+								if (i+1 >= hull_colors.size())
+								{
+									hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255));
+								}
+
+									glColor4ubv(hull_colors[i].mV);
+								LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
+
+								if (explode > 0.f)
+								{
+									gGL.popMatrix();
+								}
+							}
+						}
+					}
+
+					if (render_mesh)
+					{
+						if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
+						{
+							genBuffers(LLModel::LOD_PHYSICS, false);
+						}
+						for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i)
+						{
+							LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
+
+							buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+
+							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+							gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+							glColor4f(0.4f, 0.4f, 0.0f, 0.4f);
+
+							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+
+							glColor3f(1.f, 1.f, 0.f);
+
+							glLineWidth(3.f);
+							glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+							glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+							glLineWidth(1.f);
+						}
+					}
+
+					gGL.popMatrix();
+				}
+
+				gGL.setSceneBlendType(LLRender::BT_ALPHA);
+			}
+		}
+		else
+		{
+			LLVOAvatarSelf* avatar = gAgentAvatarp;
+			target_pos = avatar->getPositionAgent();
+
+			LLViewerCamera::getInstance()->setOriginAndLookAt(
+															  target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot),		// camera
+															  LLVector3::z_axis,																	// up
+															  target_pos);											// point of interest
+
+			if (joint_positions)
+			{
+				avatar->renderCollisionVolumes();
+			}
+
+			for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+			{
+				for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+				{
+					LLModelInstance& instance = *model_iter;
+					LLModel* model = instance.mModel;
+
+					if (!model->mSkinWeights.empty())
+					{
+						for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
+						{
+							LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+
+							const LLVolumeFace& face = model->getVolumeFace(i);
+
+							LLStrider<LLVector3> position;
+							buffer->getVertexStrider(position);
+
+							LLStrider<LLVector4> weight;
+							buffer->getWeight4Strider(weight);
+
+							//quick 'n dirty software vertex skinning
+
+							//build matrix palette
+							
+							LLMatrix4 mat[64];
+							for (U32 j = 0; j < model->mSkinInfo.mJointNames.size(); ++j)
+							{
+								LLJoint* joint = avatar->getJoint(model->mSkinInfo.mJointNames[j]);
+								if (joint)
+								{
+									mat[j] = model->mSkinInfo.mInvBindMatrix[j];
+									mat[j] *= joint->getWorldMatrix();
+								}
+							}
+
+							for (U32 j = 0; j < buffer->getRequestedVerts(); ++j)
+							{
+								LLMatrix4 final_mat;
+								final_mat.mMatrix[0][0] = final_mat.mMatrix[1][1] = final_mat.mMatrix[2][2] = final_mat.mMatrix[3][3] = 0.f;
+
+								LLVector4 wght;
+								S32 idx[4];
+
+								F32 scale = 0.f;
+								for (U32 k = 0; k < 4; k++)
+								{
+									F32 w = weight[j].mV[k];
+
+									idx[k] = (S32) floorf(w);
+									wght.mV[k] = w - floorf(w);
+									scale += wght.mV[k];
+								}
+
+								wght *= 1.f/scale;
+
+								for (U32 k = 0; k < 4; k++)
+								{
+									F32* src = (F32*) mat[idx[k]].mMatrix;
+									F32* dst = (F32*) final_mat.mMatrix;
+
+									F32 w = wght.mV[k];
+
+									for (U32 l = 0; l < 16; l++)
+									{
+										dst[l] += src[l]*w;
+									}
+								}
+
+								//VECTORIZE THIS
+								LLVector3 v(face.mPositions[j].getF32ptr());
+
+								v = v * model->mSkinInfo.mBindShapeMatrix;
+								v = v * final_mat;
+
+								position[j] = v;
+							}
+
+							buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+							glColor4fv(instance.mMaterial[i].mDiffuseColor.mV);
+							gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
+							glColor3f(0.4f, 0.4f, 0.4f);
+
+							if (edges)
+							{
+								glLineWidth(3.f);
+								glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+								buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
+								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+								glLineWidth(1.f);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	gGL.popMatrix();
+
+	return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLModelPreview::refresh()
+{
+	mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+	mCameraYaw = mCameraYaw + yaw_radians;
+
+	mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLModelPreview::zoom(F32 zoom_amt)
+{
+	F32 new_zoom = mCameraZoom+zoom_amt;
+
+	mCameraZoom	= llclamp(new_zoom, 1.f, 10.f);
+}
+
+void LLModelPreview::pan(F32 right, F32 up)
+{
+	mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
+	mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+}
+
+void LLModelPreview::setPreviewLOD(S32 lod)
+{
+	lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH);
+
+	if (lod != mPreviewLOD)
+	{
+		mPreviewLOD = lod;
+
+		LLComboBox* combo_box = mFMP->getChild<LLComboBox>("preview_lod_combo");
+		combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
+		mFMP->childSetTextArg("lod_table_footer", "[DETAIL]", mFMP->getString(lod_name[mPreviewLOD]));
+		mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]);
+
+		// the wizard has three lod drop downs
+		LLComboBox* combo_box2 = mFMP->getChild<LLComboBox>("preview_lod_combo2");
+		combo_box2->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
+		
+		LLComboBox* combo_box3 = mFMP->getChild<LLComboBox>("preview_lod_combo3");
+		combo_box3->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order
+
+		LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor");
+		LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor");
+
+		for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i)
+		{
+			const LLColor4& color = (i == lod) ? highlight_color : normal_color;
+
+			mFMP->childSetColor(lod_status_name[i], color);
+			mFMP->childSetColor(lod_label_name[i], color);
+			mFMP->childSetColor(lod_triangles_name[i], color);
+			mFMP->childSetColor(lod_vertices_name[i], color);
+		}
+
+		LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+		if (fmp)
+		{
+			LLRadioGroup* radio = fmp->getChild<LLRadioGroup>("lod_file_or_limit");
+			radio->selectNthItem(fmp->mLODMode[mPreviewLOD]);
+		}
+	}
+	refresh();
+	updateStatusMessages();
+}
+
+//static
+void LLFloaterModelPreview::onBrowseLOD(void* data)
+{
+	assert_main_thread();
+
+	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data;
+	mp->loadModel(mp->mModelPreview->mPreviewLOD);
+}
+
+//static
+void LLFloaterModelPreview::onReset(void* user_data)
+{
+	assert_main_thread();
+
+	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
+	LLModelPreview* mp = fmp->mModelPreview;
+	std::string filename = mp->mLODFile[3]; 
+	mp->loadModel(filename,3);
+}
+
+//static
+void LLFloaterModelPreview::onUpload(void* user_data)
+{
+	assert_main_thread();
+
+	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+
+	mp->mModelPreview->rebuildUploadData();
+
+	bool upload_skinweights = mp->childGetValue("upload_skin").asBoolean();
+	bool upload_joint_positions = mp->childGetValue("upload_joints").asBoolean();
+
+	mp->mModelPreview->saveUploadData(upload_skinweights, upload_joint_positions);
+
+	gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale,
+						  mp->childGetValue("upload_textures").asBoolean(), upload_skinweights, upload_joint_positions);
+
+	mp->closeFloater(false);
+}
+
+
+//static
+void LLFloaterModelPreview::onClearMaterials(void* user_data)
+{
+	LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+	mp->mModelPreview->clearMaterials();
+}
+
+//static
+void LLFloaterModelPreview::refresh(LLUICtrl* ctrl, void* user_data)
+{
+	sInstance->mModelPreview->mDirty = true;
+}
+
+void LLFloaterModelPreview::updateResourceCost()
+{
+	U32 cost = mModelPreview->mResourceCost;
+	childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",cost));
+}
+
+//static
+void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata )
+{
+	LLModelPreview* preview = (LLModelPreview*) userdata;
+	preview->refresh();
+}
+
+void LLModelPreview::onLODParamCommit(bool enforce_tri_limit)
+{
+	genLODs(mPreviewLOD, 3, enforce_tri_limit);
+	updateStatusMessages();
+	refresh();
+}
+
+LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl)
+{
+	mStage = stage;
+	mContinue = 1;
+	mModel = mdl;
+	mDecompID = &mdl->mDecompID;
+	mParams = sInstance->mDecompParams;
+
+	//copy out positions and indices
+	if (mdl)
+	{
+		U16 index_offset = 0;
+
+		mPositions.clear();
+		mIndices.clear();
+
+		//queue up vertex positions and indices
+		for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+		{
+			const LLVolumeFace& face = mdl->getVolumeFace(i);
+			if (mPositions.size() + face.mNumVertices > 65535)
+			{
+				continue;
+			}
+
+			for (U32 j = 0; j < face.mNumVertices; ++j)
+			{
+				mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+			}
+
+			for (U32 j = 0; j < face.mNumIndices; ++j)
+			{
+				mIndices.push_back(face.mIndices[j]+index_offset);
+			}
+
+			index_offset += face.mNumVertices;
+		}
+	}
+}
+
+void LLFloaterModelPreview::setStatusMessage(const std::string& msg)
+{
+	LLMutexLock lock(mStatusLock);
+	mStatusMessage = msg;
+}
+
+S32 LLFloaterModelPreview::DecompRequest::statusCallback(const char* status, S32 p1, S32 p2)
+{
+	if (mContinue)
+	{
+		setStatusMessage(llformat("%s: %d/%d", status, p1, p2));
+		if (LLFloaterModelPreview::sInstance)
+		{
+			LLFloaterModelPreview::sInstance->setStatusMessage(mStatusMessage);
+		}
+	}
+
+	return mContinue;
+}
+
+void LLFloaterModelPreview::DecompRequest::completed()
+{ //called from the main thread
+	if (mContinue)
+	{
+		mModel->setConvexHullDecomposition(mHull);
+
+		if (sInstance)
+		{
+			if (mContinue)
+			{
+				if (sInstance->mModelPreview)
+				{
+					sInstance->mModelPreview->mDirty = true;
+					LLFloaterModelPreview::sInstance->mModelPreview->refresh();
+				}
+			}
+
+			sInstance->mCurRequest.erase(this);
+		}
+	}
+	else if (sInstance)
+	{
+		llassert(sInstance->mCurRequest.find(this) == sInstance->mCurRequest.end());
+	}
+}
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d8b46807fbd847116c199b9dd425c2d05a8f6ac
--- /dev/null
+++ b/indra/newview/llfloatermodelpreview.h
@@ -0,0 +1,420 @@
+/**
+ * @file llfloatermodelpreview.h
+ * @brief LLFloaterModelPreview class definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERMODELPREVIEW_H
+#define LL_LLFLOATERMODELPREVIEW_H
+
+#include "llfloaternamedesc.h"
+
+#include "lldynamictexture.h"
+#include "llfloatermodelwizard.h"
+#include "llquaternion.h"
+#include "llmeshrepository.h"
+#include "llmodel.h"
+#include "llthread.h"
+#include "llviewermenufile.h"
+
+class LLComboBox;
+class LLJoint;
+class LLViewerJointMesh;
+class LLVOAvatar;
+class LLTextBox;
+class LLVertexBuffer;
+class LLModelPreview;
+class LLFloaterModelPreview;
+class daeElement;
+class domProfile_COMMON;
+class domInstance_geometry;
+class domNode;
+class domTranslate;
+class LLMenuButton;
+class LLToggleableMenu;
+
+typedef std::map<std::string, LLMatrix4> JointTransformMap;
+typedef std::map<std::string, LLMatrix4>:: iterator JointTransformMapIt;
+
+const S32 NUM_LOD = 4;
+
+class LLModelLoader : public LLThread
+{
+public:
+	typedef enum
+	{
+		STARTING = 0,
+		READING_FILE,
+		CREATING_FACES,
+		GENERATING_VERTEX_BUFFERS,
+		GENERATING_LOD,
+		DONE,
+		ERROR_PARSING //basically loading failed
+	} eLoadState;
+
+	U32 mState;
+	std::string mFilename;
+	S32 mLod;
+	LLModelPreview* mPreview;
+	LLMatrix4 mTransform;
+	BOOL mFirstTransform;
+	LLVector3 mExtents[2];
+	bool mTrySLM;
+	
+	std::map<daeElement*, LLPointer<LLModel> > mModel;
+	
+	typedef std::vector<LLPointer<LLModel> > model_list;
+	model_list mModelList;
+
+	typedef std::vector<LLModelInstance> model_instance_list;
+	
+	typedef std::map<LLMatrix4, model_instance_list > scene;
+
+	scene mScene;
+
+	typedef std::queue<LLPointer<LLModel> > model_queue;
+
+	//queue of models that need a physics rep
+	model_queue mPhysicsQ;
+
+	LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, 
+				   std::deque<std::string>& jointsFromNodes );
+	~LLModelLoader() ;
+
+	virtual void run();
+	bool doLoadModel();
+	bool loadFromSLM(const std::string& filename);
+	void loadModelCallback();
+
+	void loadTextures() ; //called in the main thread.
+	void processElement(daeElement* element);
+	std::vector<LLImportMaterial> getMaterials(LLModel* model, domInstance_geometry* instance_geo);
+	LLImportMaterial profileToMaterial(domProfile_COMMON* material);
+	std::string getElementLabel(daeElement *element);
+	LLColor4 getDaeColor(daeElement* element);
+	
+	daeElement* getChildFromElement( daeElement* pElement, std::string const & name );
+	
+	bool isNodeAJoint( domNode* pNode );
+	void processJointNode( domNode* pNode, std::map<std::string,LLMatrix4>& jointTransforms );
+	void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform );
+	void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform );
+	
+	void setLoadState(U32 state);
+
+	void buildJointToNodeMappingFromScene( daeElement* pRoot );
+	void processJointToNodeMapping( domNode* pNode );
+
+
+	//map of avatar joints as named in COLLADA assets to internal joint names
+	std::map<std::string, std::string> mJointMap;
+	JointTransformMap& mJointList;	
+	std::deque<std::string>& mJointsFromNode;
+
+private:
+	static std::list<LLModelLoader*> sActiveLoaderList;
+	static bool isAlive(LLModelLoader* loader) ;
+};
+
+class LLFloaterModelPreview : public LLFloater
+{
+public:
+	
+	class DecompRequest : public LLPhysicsDecomp::Request
+	{
+	public:
+		S32 mContinue;
+		LLPointer<LLModel> mModel;
+		
+		DecompRequest(const std::string& stage, LLModel* mdl);
+		virtual S32 statusCallback(const char* status, S32 p1, S32 p2);
+		virtual void completed();
+		
+	};
+	static LLFloaterModelPreview* sInstance;
+	
+	LLFloaterModelPreview(const LLSD& key);
+	virtual ~LLFloaterModelPreview();
+	
+	virtual BOOL postBuild();
+	
+	BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+	BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+	BOOL handleHover(S32 x, S32 y, MASK mask);
+	BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); 
+	
+	static void onMouseCaptureLostModelPreview(LLMouseHandler*);
+	static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+
+	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
+	
+	static void onBrowseLOD(void* data);
+	
+	static void onReset(void* data);
+
+	static void onUpload(void* data);
+	
+	static void onClearMaterials(void* data);
+	
+	static void refresh(LLUICtrl* ctrl, void* data);
+	
+	void updateResourceCost();
+	
+	void			loadModel(S32 lod);
+	
+	void onViewOptionChecked(const LLSD& userdata);
+	bool isViewOptionChecked(const LLSD& userdata);
+	bool isViewOptionEnabled(const LLSD& userdata);
+	void setViewOptionEnabled(const std::string& option, bool enabled);
+	void enableViewOption(const std::string& option);
+	void disableViewOption(const std::string& option);
+
+protected:
+	friend class LLModelPreview;
+	friend class LLMeshFilePicker;
+	friend class LLPhysicsDecomp;
+	
+	static void		onImportScaleCommit(LLUICtrl*, void*);
+	static void		onPelvisOffsetCommit(LLUICtrl*, void*);
+	static void		onUploadJointsCommit(LLUICtrl*,void*);
+	static void		onUploadSkinCommit(LLUICtrl*,void*);
+	
+	static void		onPreviewLODCommit(LLUICtrl*,void*);
+	
+	static void		onGenerateNormalsCommit(LLUICtrl*,void*);
+	
+	static void		onAutoFillCommit(LLUICtrl*,void*);
+	static void		onLODParamCommit(LLUICtrl*,void*);
+	static void		onLODParamCommitTriangleLimit(LLUICtrl*,void*);
+	
+	static void		onExplodeCommit(LLUICtrl*, void*);
+	
+	static void onPhysicsParamCommit(LLUICtrl* ctrl, void* userdata);
+	static void onPhysicsStageExecute(LLUICtrl* ctrl, void* userdata);
+	static void onCancel(LLUICtrl* ctrl, void* userdata);
+	static void onPhysicsStageCancel(LLUICtrl* ctrl, void* userdata);
+	
+	static void onPhysicsBrowse(LLUICtrl* ctrl, void* userdata);
+	static void onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata);
+	static void onPhysicsOptimize(LLUICtrl* ctrl, void* userdata);
+	static void onPhysicsDecomposeBack(LLUICtrl* ctrl, void* userdata);
+	static void onPhysicsSimplifyBack(LLUICtrl* ctrl, void* userdata);
+		
+	void			draw();
+	
+	void initDecompControls();
+	
+	void setStatusMessage(const std::string& msg);
+
+	LLModelPreview*	mModelPreview;
+	
+	LLPhysicsDecomp::decomp_params mDecompParams;
+	
+	S32				mLastMouseX;
+	S32				mLastMouseY;
+	LLRect			mPreviewRect;
+	U32				mGLName;
+	static S32		sUploadAmount;
+	
+	std::set<LLPointer<DecompRequest> > mCurRequest;
+	std::string mStatusMessage;
+
+	//use "disabled" as false by default
+	std::map<std::string, bool> mViewOptionDisabled;
+	
+	//store which lod mode each LOD is using
+	// 0 - load from file
+	// 1 - auto generate
+	// 2 - None
+	S32 mLODMode[4];
+
+	LLMenuButton* mViewOptionMenuButton;
+	LLToggleableMenu* mViewOptionMenu;
+	LLMutex* mStatusLock;
+
+};
+
+class LLMeshFilePicker : public LLFilePickerThread
+{
+public:
+	LLMeshFilePicker(LLModelPreview* mp, S32 lod);
+	virtual void notify(const std::string& filename);
+
+private:
+	LLModelPreview* mMP;
+	S32 mLOD;
+};
+
+
+class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
+{	
+	typedef boost::signals2::signal<void (F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)> details_signal_t;
+	typedef boost::signals2::signal<void (void)> model_loaded_signal_t;
+
+public:
+	LLModelPreview(S32 width, S32 height, LLFloater* fmp);
+	virtual ~LLModelPreview();
+
+	void resetPreviewTarget();
+	void setPreviewTarget(F32 distance);
+	void setTexture(U32 name) { mTextureName = name; }
+
+	void setPhysicsFromLOD(S32 lod);
+	BOOL render();
+	void update();
+	void genBuffers(S32 lod, bool skinned);
+	void clearBuffers();
+	void refresh();
+	void rotate(F32 yaw_radians, F32 pitch_radians);
+	void zoom(F32 zoom_amt);
+	void pan(F32 right, F32 up);
+	virtual BOOL needsRender() { return mNeedsUpdate; }
+	void setPreviewLOD(S32 lod);
+	void clearModel(S32 lod);
+	void loadModel(std::string filename, S32 lod);
+	void loadModelCallback(S32 lod);
+	void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false);
+	void generateNormals();
+	void clearMaterials();
+	U32 calcResourceCost();
+	void rebuildUploadData();
+	void saveUploadData(bool save_skinweights, bool save_joint_poisitions);
+	void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_poisitions);
+	void clearIncompatible(S32 lod);
+	void updateStatusMessages();
+	void clearGLODGroup();
+	void onLODParamCommit(bool enforce_tri_limit);
+
+	const bool getModelPivot( void ) const { return mHasPivot; }
+	void setHasPivot( bool val ) { mHasPivot = val; }
+	void setModelPivot( const LLVector3& pivot ) { mModelPivot = pivot; }
+
+	//Sets the current avatars joints to new positions
+	//Makes in world go to shit, however
+	void changeAvatarsJointPositions( LLModel* pModel );
+	//Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
+	void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset );
+	void critiqueJointToNodeMappingFromScene( void  );
+	//Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions
+	//Accessors for joint position upload friendly rigs
+	const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
+	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
+	bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset );
+	//Determines if a rig is a legacy from the joint list
+	bool isRigLegacy( const std::vector<std::string> &jointListFromAsset );	
+	//Accessors for the legacy rigs
+	const bool isLegacyRigValid( void ) const { return mLegacyRigValid; }
+	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }	
+
+	static void	textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata );
+	
+	boost::signals2::connection setDetailsCallback( const details_signal_t::slot_type& cb ){  return mDetailsSignal.connect(cb);  }
+	boost::signals2::connection setModelLoadedCallback( const model_loaded_signal_t::slot_type& cb ){  return mModelLoadedSignal.connect(cb);  }
+	
+	void setLoadState( U32 state ) { mLoadState = state; }
+	U32 getLoadState() { return mLoadState; }
+	//setRestJointFlag: If an asset comes through that changes the joints, we want the reset to persist
+	void setResetJointFlag( bool state ) { if ( !mResetJoints ) mResetJoints = state; }
+	const bool getResetJointFlag( void ) const { return mResetJoints; }
+	void setRigWithSceneParity( bool state ) { mRigParityWithScene = state; }
+	const bool getRigWithSceneParity( void ) const { return mRigParityWithScene; }
+	
+	LLVector3 getTranslationForJointOffset( std::string joint );
+
+ protected:
+	friend class LLModelLoader;
+	friend class LLFloaterModelPreview;
+	friend class LLFloaterModelWizard;
+	friend class LLFloaterModelPreview::DecompRequest;
+	friend class LLFloaterModelWizard::DecompRequest;
+	friend class LLPhysicsDecomp;
+
+	LLFloater*  mFMP;
+
+	BOOL        mNeedsUpdate;
+	bool		mDirty;
+	bool		mGenLOD;
+	U32         mTextureName;
+	F32			mCameraDistance;
+	F32			mCameraYaw;
+	F32			mCameraPitch;
+	F32			mCameraZoom;
+	LLVector3	mCameraOffset;
+	LLVector3	mPreviewTarget;
+	LLVector3	mPreviewScale;
+	S32			mPreviewLOD;
+	U32			mResourceCost;
+	std::string mLODFile[LLModel::NUM_LODS];
+	bool		mLoading;
+	U32			mLoadState;
+	bool		mResetJoints;
+	bool		mRigParityWithScene;
+	
+	std::map<std::string, bool> mViewOption;
+
+	//GLOD object parameters (must rebuild object if these change)
+	F32 mBuildShareTolerance;
+	U32 mBuildQueueMode;
+	U32 mBuildOperator;
+	U32 mBuildBorderMode;
+	S32 mRequestedTriangleCount[LLModel::NUM_LODS];
+
+	
+	LLModelLoader* mModelLoader;
+
+	LLModelLoader::scene mScene[LLModel::NUM_LODS];
+	LLModelLoader::scene mBaseScene;
+
+	LLModelLoader::model_list mModel[LLModel::NUM_LODS];
+	LLModelLoader::model_list mBaseModel;
+
+	U32 mGroup;
+	std::map<LLPointer<LLModel>, U32> mObject;
+	U32 mMaxTriangleLimit;
+	
+	LLMeshUploadThread::instance_list mUploadData;
+	std::set<LLViewerFetchedTexture* > mTextureSet;
+
+	//map of vertex buffers to models (one vertex buffer in vector per face in model
+	std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS+1];
+
+	details_signal_t mDetailsSignal;
+	model_loaded_signal_t mModelLoadedSignal;
+	
+	LLVector3	mModelPivot;
+	bool		mHasPivot;
+	
+	float		mPelvisZOffset;
+	
+	bool		mRigValidJointUpload;
+	bool		mLegacyRigValid;
+
+	bool		mLastJointUpdate;
+
+	std::deque<std::string> mMasterJointList;
+	std::deque<std::string> mMasterLegacyJointList;
+	std::deque<std::string> mJointsFromNode;
+	JointTransformMap		mJointTransformMap;
+};
+
+#endif  // LL_LLFLOATERMODELPREVIEW_H
diff --git a/indra/newview/llfloatermodelwizard.cpp b/indra/newview/llfloatermodelwizard.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..faf81dbc5c3cef62fb519439de92b04c2c101c41
--- /dev/null
+++ b/indra/newview/llfloatermodelwizard.cpp
@@ -0,0 +1,679 @@
+/** 
+ * @file llfloatermodelwizard.cpp
+ * @author Leyla Farazha
+ * @brief Implementation of the LLFloaterModelWizard class.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llbutton.h"
+#include "lldrawable.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfloater.h"
+#include "llfloatermodelwizard.h"
+#include "llfloatermodelpreview.h"
+#include "llfloaterreg.h"
+#include "llsliderctrl.h"
+#include "lltoolmgr.h"
+#include "llviewerwindow.h"
+
+LLFloaterModelWizard* LLFloaterModelWizard::sInstance = NULL;
+
+static	const std::string stateNames[]={
+	"choose_file",
+	"optimize",
+	"physics",
+	"physics2",
+	"review",
+	"upload"};
+
+LLFloaterModelWizard::LLFloaterModelWizard(const LLSD& key)
+	: LLFloater(key)
+{
+	mLastEnabledState = CHOOSE_FILE;
+	sInstance = this;
+
+	mCommitCallbackRegistrar.add("Wizard.Choose", boost::bind(&LLFloaterModelWizard::setState, this, CHOOSE_FILE));
+	mCommitCallbackRegistrar.add("Wizard.Optimize", boost::bind(&LLFloaterModelWizard::setState, this, OPTIMIZE));
+	mCommitCallbackRegistrar.add("Wizard.Physics", boost::bind(&LLFloaterModelWizard::setState, this, PHYSICS));
+	mCommitCallbackRegistrar.add("Wizard.Physics2", boost::bind(&LLFloaterModelWizard::setState, this, PHYSICS2));
+	mCommitCallbackRegistrar.add("Wizard.Review", boost::bind(&LLFloaterModelWizard::setState, this, REVIEW));
+	mCommitCallbackRegistrar.add("Wizard.Upload", boost::bind(&LLFloaterModelWizard::setState, this, UPLOAD));
+}
+LLFloaterModelWizard::~LLFloaterModelWizard()
+{
+	sInstance = NULL;
+}
+void LLFloaterModelWizard::setState(int state)
+{
+
+	mState = state;
+
+	for(size_t t=0; t<LL_ARRAY_SIZE(stateNames); ++t)
+	{
+		LLView *view = getChildView(stateNames[t]+"_panel");
+		if (view) 
+		{
+			view->setVisible(state == (int) t ? TRUE : FALSE);
+		}
+	}
+
+	if (state == CHOOSE_FILE)
+	{
+		mModelPreview->mViewOption["show_physics"] = false;
+
+		getChildView("close")->setVisible(false);
+		getChildView("back")->setVisible(true);
+		getChildView("back")->setEnabled(false);
+		getChildView("next")->setVisible(true);
+		getChildView("upload")->setVisible(false);
+		getChildView("cancel")->setVisible(true);
+	}
+
+	if (state == OPTIMIZE)
+	{
+		if (mLastEnabledState < state)
+		{			
+			mModelPreview->genLODs(-1);
+		}
+
+		mModelPreview->mViewOption["show_physics"] = false;
+
+		getChildView("back")->setVisible(true);
+		getChildView("back")->setEnabled(true);
+		getChildView("close")->setVisible(false);
+		getChildView("next")->setVisible(true);
+		getChildView("upload")->setVisible(false);
+		getChildView("cancel")->setVisible(true);
+	}
+
+	if (state == PHYSICS)
+	{
+		if (mLastEnabledState < state)
+		{
+			mModelPreview->setPhysicsFromLOD(1);
+		}
+
+		mModelPreview->mViewOption["show_physics"] = true;
+
+		getChildView("next")->setVisible(true);
+		getChildView("upload")->setVisible(false);
+		getChildView("close")->setVisible(false);
+		getChildView("back")->setVisible(true);
+		getChildView("back")->setEnabled(true);
+		getChildView("cancel")->setVisible(true);
+	}
+
+	if (state == PHYSICS2)
+	{
+		if (mLastEnabledState < state)
+		{
+			executePhysicsStage("Decompose");
+		}
+
+		mModelPreview->mViewOption["show_physics"] = true;
+
+		getChildView("next")->setVisible(true);
+		getChildView("next")->setEnabled(true);
+		getChildView("upload")->setVisible(false);
+		getChildView("close")->setVisible(false);
+		getChildView("back")->setVisible(true);
+		getChildView("back")->setEnabled(true);
+		getChildView("cancel")->setVisible(true);
+	}
+
+	if (state == REVIEW)
+	{
+		
+		mModelPreview->mViewOption["show_physics"] = false;
+
+		getChildView("close")->setVisible(false);
+		getChildView("next")->setVisible(false);
+		getChildView("back")->setVisible(true);
+		getChildView("back")->setEnabled(true);
+		getChildView("upload")->setVisible(true);
+		getChildView("cancel")->setVisible(true);
+	}
+
+	if (state == UPLOAD)
+	{
+		getChildView("close")->setVisible(true);
+		getChildView("next")->setVisible(false);
+		getChildView("back")->setVisible(false);
+		getChildView("upload")->setVisible(false);
+		getChildView("cancel")->setVisible(false);
+	}
+
+	updateButtons();
+}
+
+
+
+void LLFloaterModelWizard::updateButtons()
+{
+	if (mLastEnabledState < mState)
+	{
+		mLastEnabledState = mState;
+	}
+
+	for(size_t i=0; i<LL_ARRAY_SIZE(stateNames); ++i)
+	{
+		LLButton *button = getChild<LLButton>(stateNames[i]+"_btn");
+
+		if (i == mState)
+		{
+			button->setEnabled(TRUE);
+			button->setToggleState(TRUE);
+		}
+		else if (i <= mLastEnabledState)
+		{
+			button->setEnabled(TRUE);
+			button->setToggleState(FALSE);
+		}
+		else
+		{
+			button->setEnabled(FALSE);
+		}
+	}
+
+	LLButton *physics_button = getChild<LLButton>(stateNames[PHYSICS]+"_btn");
+	
+	if (mState == PHYSICS2)
+	{
+		physics_button->setVisible(false);
+	}
+	else
+	{
+		physics_button->setVisible(true);
+	}
+
+}
+
+void LLFloaterModelWizard::loadModel()
+{
+	 mModelPreview->mLoading = TRUE;
+	
+	(new LLMeshFilePicker(mModelPreview, 3))->getFile();
+}
+
+void LLFloaterModelWizard::onClickCancel()
+{
+	closeFloater();
+}
+
+void LLFloaterModelWizard::onClickBack()
+{
+	setState(llmax((int) CHOOSE_FILE, mState-1));
+}
+
+void LLFloaterModelWizard::onClickNext()
+{
+	setState(llmin((int) UPLOAD, mState+1));
+}
+
+bool LLFloaterModelWizard::onEnableNext()
+{
+	return true;
+}
+
+bool LLFloaterModelWizard::onEnableBack()
+{
+	return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelWizard::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+	if (mPreviewRect.pointInRect(x, y))
+	{
+		bringToFront( x, y );
+		gFocusMgr.setMouseCapture(this);
+		gViewerWindow->hideCursor();
+		mLastMouseX = x;
+		mLastMouseY = y;
+		return TRUE;
+	}
+	
+	return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelWizard::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+	gFocusMgr.setMouseCapture(FALSE);
+	gViewerWindow->showCursor();
+	return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelWizard::handleHover	(S32 x, S32 y, MASK mask)
+{
+	MASK local_mask = mask & ~MASK_ALT;
+	
+	if (mModelPreview && hasMouseCapture())
+	{
+		if (local_mask == MASK_PAN)
+		{
+			// pan here
+			mModelPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+		}
+		else if (local_mask == MASK_ORBIT)
+		{
+			F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+			F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+			
+			mModelPreview->rotate(yaw_radians, pitch_radians);
+		}
+		else 
+		{
+			
+			F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+			F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+			
+			mModelPreview->rotate(yaw_radians, 0.f);
+			mModelPreview->zoom(zoom_amt);
+		}
+		
+		
+		mModelPreview->refresh();
+		
+		LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY);
+	}
+	
+	if (!mPreviewRect.pointInRect(x, y) || !mModelPreview)
+	{
+		return LLFloater::handleHover(x, y, mask);
+	}
+	else if (local_mask == MASK_ORBIT)
+	{
+		gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+	}
+	else if (local_mask == MASK_PAN)
+	{
+		gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+	}
+	else
+	{
+		gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+	}
+	
+	return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelWizard::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+	if (mPreviewRect.pointInRect(x, y) && mModelPreview)
+	{
+		mModelPreview->zoom((F32)clicks * -0.2f);
+		mModelPreview->refresh();
+	}
+	
+	return TRUE;
+}
+
+void LLFloaterModelWizard::initDecompControls()
+{
+	LLSD key;
+
+	static const LLCDStageData* stage = NULL;
+	static S32 stage_count = 0;
+
+	if (!stage && LLConvexDecomposition::getInstance() != NULL)
+	{
+		stage_count = LLConvexDecomposition::getInstance()->getStages(&stage);
+	}
+
+	static const LLCDParam* param = NULL;
+	static S32 param_count = 0;
+	if (!param && LLConvexDecomposition::getInstance() != NULL)
+	{
+		param_count = LLConvexDecomposition::getInstance()->getParameters(&param);
+	}
+
+	for (S32 j = stage_count-1; j >= 0; --j)
+	{
+		gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j;
+		// protected against stub by stage_count being 0 for stub above
+		LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback);
+
+		for (S32 i = 0; i < param_count; ++i)
+		{
+			if (param[i].mStage != j)
+			{
+				continue;
+			}
+
+			std::string name(param[i].mName ? param[i].mName : "");
+			std::string description(param[i].mDescription ? param[i].mDescription : "");
+
+			if (param[i].mType == LLCDParam::LLCD_FLOAT)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mFloat);
+			}
+			else if (param[i].mType == LLCDParam::LLCD_INTEGER)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue);
+			}
+			else if (param[i].mType == LLCDParam::LLCD_BOOLEAN)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mBool);
+			}
+			else if (param[i].mType == LLCDParam::LLCD_ENUM)
+			{
+				mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue);
+			}
+		}
+	}
+
+	mDecompParams["Simplify Method"] = 0; // set it to retain %
+}
+
+//static
+void LLFloaterModelWizard::executePhysicsStage(std::string stage_name)
+{
+	if (sInstance)
+	{
+		F64 physics_accuracy = sInstance->getChild<LLSliderCtrl>("physics_slider")->getValue().asReal();
+
+		sInstance->mDecompParams["Retain%"] = physics_accuracy;
+
+		if (!sInstance->mCurRequest.empty())
+		{
+			llinfos << "Decomposition request still pending." << llendl;
+			return;
+		}
+
+		if (sInstance->mModelPreview)
+		{
+			for (S32 i = 0; i < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size(); ++i)
+			{
+				LLModel* mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][i];
+				DecompRequest* request = new DecompRequest(stage_name, mdl);
+				sInstance->mCurRequest.insert(request);
+				gMeshRepo.mDecompThread->submitRequest(request);
+			}
+		}
+	}
+}
+
+LLFloaterModelWizard::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl)
+{
+	mStage = stage;
+	mContinue = 1;
+	mModel = mdl;
+	mDecompID = &mdl->mDecompID;
+	mParams = sInstance->mDecompParams;
+
+	//copy out positions and indices
+	if (mdl)
+	{
+		U16 index_offset = 0;
+
+		mPositions.clear();
+		mIndices.clear();
+
+		//queue up vertex positions and indices
+		for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+		{
+			const LLVolumeFace& face = mdl->getVolumeFace(i);
+			if (mPositions.size() + face.mNumVertices > 65535)
+			{
+				continue;
+			}
+
+			for (U32 j = 0; j < face.mNumVertices; ++j)
+			{
+				mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+			}
+
+			for (U32 j = 0; j < face.mNumIndices; ++j)
+			{
+				mIndices.push_back(face.mIndices[j]+index_offset);
+			}
+
+			index_offset += face.mNumVertices;
+		}
+	}
+}
+
+
+S32 LLFloaterModelWizard::DecompRequest::statusCallback(const char* status, S32 p1, S32 p2)
+{
+	setStatusMessage(llformat("%s: %d/%d", status, p1, p2));
+
+	return mContinue;
+}
+
+void LLFloaterModelWizard::DecompRequest::completed()
+{ //called from the main thread
+	mModel->setConvexHullDecomposition(mHull);
+
+	if (sInstance)
+	{
+		if (sInstance->mModelPreview)
+		{
+			sInstance->mModelPreview->mDirty = true;
+			LLFloaterModelWizard::sInstance->mModelPreview->refresh();
+		}
+
+		sInstance->mCurRequest.erase(this);
+	}
+
+	if (mStage == "Decompose")
+	{
+		executePhysicsStage("Simplify");
+	}
+}
+
+
+BOOL LLFloaterModelWizard::postBuild()
+{
+	LLView* preview_panel = getChildView("preview_panel");
+
+	childSetValue("import_scale", (F32) 0.67335826);
+
+	getChild<LLUICtrl>("browse")->setCommitCallback(boost::bind(&LLFloaterModelWizard::loadModel, this));
+	//getChild<LLUICtrl>("lod_file")->setCommitCallback(boost::bind(&LLFloaterModelWizard::loadModel, this));
+	getChild<LLUICtrl>("cancel")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickCancel, this));
+	getChild<LLUICtrl>("close")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickCancel, this));
+	getChild<LLUICtrl>("back")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickBack, this));
+	getChild<LLUICtrl>("next")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickNext, this));
+	getChild<LLUICtrl>("preview_lod_combo")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onPreviewLODCommit, this, _1));
+	getChild<LLUICtrl>("preview_lod_combo2")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onPreviewLODCommit, this, _1));
+	getChild<LLUICtrl>("preview_lod_combo3")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onPreviewLODCommit, this, _1));
+	getChild<LLUICtrl>("accuracy_slider")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onAccuracyPerformance, this, _2));
+	getChild<LLUICtrl>("upload")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onUpload, this));
+	getChild<LLUICtrl>("physics_slider")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onPhysicsChanged, this));
+
+	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+	
+	enable_registrar.add("Next.OnEnable", boost::bind(&LLFloaterModelWizard::onEnableNext, this));
+	enable_registrar.add("Back.OnEnable", boost::bind(&LLFloaterModelWizard::onEnableBack, this));
+
+
+	mPreviewRect = preview_panel->getRect();
+	
+	mModelPreview = new LLModelPreview(512, 512, this);
+	mModelPreview->setPreviewTarget(16.f);
+	mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelWizard::setDetails, this, _1, _2, _3, _4, _5));
+	mModelPreview->setModelLoadedCallback(boost::bind(&LLFloaterModelWizard::modelLoadedCallback, this));
+	mModelPreview->mViewOption["show_textures"] = true;
+
+	center();
+
+	setState(CHOOSE_FILE);
+
+	childSetTextArg("import_dimensions", "[X]", LLStringUtil::null);
+	childSetTextArg("import_dimensions", "[Y]", LLStringUtil::null);
+	childSetTextArg("import_dimensions", "[Z]", LLStringUtil::null);
+
+	initDecompControls();
+
+	return TRUE;
+}
+
+
+void LLFloaterModelWizard::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)
+{
+	// iterate through all the panels, setting the dimensions
+	for(size_t t=0; t<LL_ARRAY_SIZE(stateNames); ++t)
+	{
+		LLPanel *panel = getChild<LLPanel>(stateNames[t]+"_panel");
+		if (panel) 
+		{
+			panel->childSetText("dimension_x", llformat("%.1f", x));
+			panel->childSetText("dimension_y", llformat("%.1f", y));
+			panel->childSetText("dimension_z", llformat("%.1f", z));
+			panel->childSetTextArg("streaming cost", "[COST]", llformat("%.3f", streaming_cost));
+			panel->childSetTextArg("physics cost", "[COST]", llformat("%.3f", physics_cost));	
+		}
+	}
+}
+
+void LLFloaterModelWizard::modelLoadedCallback()
+{
+	mLastEnabledState = CHOOSE_FILE;
+	getChild<LLCheckBoxCtrl>("confirm_checkbox")->set(FALSE);
+	updateButtons();
+}
+
+void LLFloaterModelWizard::onPhysicsChanged()
+{
+	mLastEnabledState = PHYSICS;
+	updateButtons();
+}
+
+void LLFloaterModelWizard::onUpload()
+{	
+	mModelPreview->rebuildUploadData();
+	
+	gMeshRepo.uploadModel(mModelPreview->mUploadData, mModelPreview->mPreviewScale, 
+						  true, false, false);
+	
+	setState(UPLOAD);
+	
+}
+
+void LLFloaterModelWizard::onAccuracyPerformance(const LLSD& data)
+{
+	int val = (int) data.asInteger();
+
+	mModelPreview->genLODs(-1, NUM_LOD-val);
+
+	mModelPreview->refresh();
+}
+
+
+void LLFloaterModelWizard::onPreviewLODCommit(LLUICtrl* ctrl)
+{
+	if (!mModelPreview)
+	{
+		return;
+	}
+	
+	S32 which_mode = 0;
+	
+	LLComboBox* combo = (LLComboBox*) ctrl;
+	
+	which_mode = (NUM_LOD-1)-combo->getFirstSelectedIndex(); // combo box list of lods is in reverse order
+
+	mModelPreview->setPreviewLOD(which_mode);
+}
+
+void LLFloaterModelWizard::refresh()
+{
+	if (mState == CHOOSE_FILE)
+	{
+		bool model_loaded = false;
+
+		if (mModelPreview && mModelPreview->getLoadState() == LLModelLoader::DONE)
+		{
+			model_loaded = true;
+		}
+		
+		getChildView("next")->setEnabled(model_loaded);
+	}
+	if (mState == REVIEW)
+	{
+		getChildView("upload")->setEnabled(getChild<LLCheckBoxCtrl>("confirm_checkbox")->getValue().asBoolean());
+	}
+
+}
+
+void LLFloaterModelWizard::draw()
+{
+	refresh();
+
+	LLFloater::draw();
+	LLRect r = getRect();
+	
+	mModelPreview->update();
+
+	if (mModelPreview)
+	{
+		gGL.color3f(1.f, 1.f, 1.f);
+		
+		gGL.getTexUnit(0)->bind(mModelPreview);
+		
+		LLView *view = getChildView(stateNames[mState]+"_panel");
+		LLView* preview_panel = view->getChildView("preview_panel");
+
+		LLRect rect = preview_panel->getRect();
+		if (rect != mPreviewRect)
+		{
+			mModelPreview->refresh();
+			mPreviewRect = preview_panel->getRect();
+		}
+		
+		LLRect item_rect;
+		preview_panel->localRectToOtherView(preview_panel->getLocalRect(), &item_rect, this);
+	
+		gGL.begin( LLRender::QUADS );
+		{
+			gGL.texCoord2f(0.f, 1.f);
+			gGL.vertex2i(item_rect.mLeft, item_rect.mTop-1);
+			gGL.texCoord2f(0.f, 0.f);
+			gGL.vertex2i(item_rect.mLeft, item_rect.mBottom);
+			gGL.texCoord2f(1.f, 0.f);
+			gGL.vertex2i(item_rect.mRight-1, item_rect.mBottom);
+			gGL.texCoord2f(1.f, 1.f);
+			gGL.vertex2i(item_rect.mRight-1, item_rect.mTop-1);
+		}
+		gGL.end();
+		
+		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+	}
+}
diff --git a/indra/newview/llfloatermodelwizard.h b/indra/newview/llfloatermodelwizard.h
new file mode 100644
index 0000000000000000000000000000000000000000..b166d262957cda56ea19b470ccef545516e5fbd1
--- /dev/null
+++ b/indra/newview/llfloatermodelwizard.h
@@ -0,0 +1,113 @@
+/** 
+ * @file llfloatermodelwizard.h
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLFLOATERMODELWIZARD_H
+#define LLFLOATERMODELWIZARD_H
+
+
+#include "llmeshrepository.h"
+#include "llmodel.h"
+#include "llthread.h"
+
+
+class LLModelPreview;
+
+
+class LLFloaterModelWizard : public LLFloater
+{
+public:
+	
+	class DecompRequest : public LLPhysicsDecomp::Request
+	{
+	public:
+		S32 mContinue;
+		LLPointer<LLModel> mModel;
+		
+		DecompRequest(const std::string& stage, LLModel* mdl);
+		virtual S32 statusCallback(const char* status, S32 p1, S32 p2);
+		virtual void completed();
+		
+	};
+	
+	static LLFloaterModelWizard* sInstance;
+
+	LLFloaterModelWizard(const LLSD& key);
+	virtual ~LLFloaterModelWizard();
+	/*virtual*/	BOOL	postBuild();
+	void			draw();
+	void            refresh();
+	
+	BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+	BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+	BOOL handleHover(S32 x, S32 y, MASK mask);
+	BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); 
+
+	void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost);
+	void modelLoadedCallback();
+	void onPhysicsChanged();
+	void initDecompControls();
+	
+	LLPhysicsDecomp::decomp_params mDecompParams;
+	std::set<LLPointer<DecompRequest> > mCurRequest;
+	std::string mStatusMessage;
+	static void executePhysicsStage(std::string stage_name);
+
+private:
+	enum EWizardState
+	{
+		CHOOSE_FILE = 0,
+		OPTIMIZE,
+		PHYSICS,
+		PHYSICS2,
+		REVIEW,
+		UPLOAD
+	};
+
+	void setState(int state);
+	void updateButtons();
+	void onClickCancel();
+	void onClickBack();
+	void onClickNext();
+	bool onEnableNext();
+	bool onEnableBack();
+	void loadModel();
+	void onPreviewLODCommit(LLUICtrl*);
+	void onAccuracyPerformance(const LLSD& data);
+	void onUpload();
+
+	LLModelPreview*	mModelPreview;
+	LLRect			mPreviewRect;
+	int mState;
+
+	S32				mLastMouseX;
+	S32				mLastMouseY;
+
+	U32			    mLastEnabledState;
+
+
+};
+
+
+#endif
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index c7fce83b03117a8afc61086c316612da60506488..4b15695cbf6eca93c8a0ff42f127e8732804273a 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -987,9 +987,15 @@ void LLFloaterPreference::refreshEnabledState()
 	LLCheckBoxCtrl* ctrl_avatar_vp = getChild<LLCheckBoxCtrl>("AvatarVertexProgram");
 	// Avatar Render Mode
 	LLCheckBoxCtrl* ctrl_avatar_cloth = getChild<LLCheckBoxCtrl>("AvatarCloth");
+	
+	bool avatar_vp_enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderAvatarVP");
+	if (LLViewerShaderMgr::sInitialized)
+	{
+		S32 max_avatar_shader = LLViewerShaderMgr::instance()->mMaxAvatarShaderLevel;
+		avatar_vp_enabled = (max_avatar_shader > 0) ? TRUE : FALSE;
+	}
 
-	S32 max_avatar_shader = LLViewerShaderMgr::instance()->mMaxAvatarShaderLevel;
-	ctrl_avatar_vp->setEnabled((max_avatar_shader > 0) ? TRUE : FALSE);
+	ctrl_avatar_vp->setEnabled(avatar_vp_enabled);
 	
 	if (gSavedSettings.getBOOL("VertexShaderEnable") == FALSE || 
 		gSavedSettings.getBOOL("RenderAvatarVP") == FALSE)
@@ -1006,7 +1012,7 @@ void LLFloaterPreference::refreshEnabledState()
 	LLCheckBoxCtrl* ctrl_shader_enable   = getChild<LLCheckBoxCtrl>("BasicShaders");
 	// radio set for terrain detail mode
 	LLRadioGroup*   mRadioTerrainDetail = getChild<LLRadioGroup>("TerrainDetailRadio");   // can be linked with control var
-
+	
 	ctrl_shader_enable->setEnabled(LLFeatureManager::getInstance()->isFeatureAvailable("VertexShaderEnable"));
 	
 	BOOL shaders = ctrl_shader_enable->get();
@@ -1029,26 +1035,28 @@ void LLFloaterPreference::refreshEnabledState()
 
 	//Deferred/SSAO/Shadows
 	LLCheckBoxCtrl* ctrl_deferred = getChild<LLCheckBoxCtrl>("UseLightShaders");
-	if (LLFeatureManager::getInstance()->isFeatureAvailable("RenderUseFBO") &&
-	    LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") &&
-		shaders)
-	{
-		BOOL enabled = (ctrl_wind_light->get()) ? TRUE : FALSE;
+	
+	BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && 
+						shaders && 
+						gGLManager.mHasFramebufferObject &&
+						gSavedSettings.getBOOL("RenderAvatarVP") &&
+						(ctrl_wind_light->get()) ? TRUE : FALSE;
 
-		ctrl_deferred->setEnabled(enabled);
+	ctrl_deferred->setEnabled(enabled);
 	
-		LLCheckBoxCtrl* ctrl_ssao = getChild<LLCheckBoxCtrl>("UseSSAO");
-		LLComboBox* ctrl_shadow = getChild<LLComboBox>("ShadowDetail");
+	LLCheckBoxCtrl* ctrl_ssao = getChild<LLCheckBoxCtrl>("UseSSAO");
+	LLCheckBoxCtrl* ctrl_dof = getChild<LLCheckBoxCtrl>("UseDoF");
+	LLComboBox* ctrl_shadow = getChild<LLComboBox>("ShadowDetail");
 
-		enabled = enabled && LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferredSSAO") && (ctrl_deferred->get() ? TRUE : FALSE);
+	enabled = enabled && LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferredSSAO") && (ctrl_deferred->get() ? TRUE : FALSE);
 		
-		ctrl_ssao->setEnabled(enabled);
+	ctrl_ssao->setEnabled(enabled);
+	ctrl_dof->setEnabled(enabled);
 
-		enabled = enabled && LLFeatureManager::getInstance()->isFeatureAvailable("RenderShadowDetail");
-
-		ctrl_shadow->setEnabled(enabled);
-	}
+	enabled = enabled && LLFeatureManager::getInstance()->isFeatureAvailable("RenderShadowDetail");
 
+	ctrl_shadow->setEnabled(enabled);
+	
 
 	// now turn off any features that are unavailable
 	disableUnavailableSettings();
@@ -1067,6 +1075,7 @@ void LLFloaterPreference::disableUnavailableSettings()
 	LLCheckBoxCtrl* ctrl_deferred = getChild<LLCheckBoxCtrl>("UseLightShaders");
 	LLComboBox* ctrl_shadows = getChild<LLComboBox>("ShadowDetail");
 	LLCheckBoxCtrl* ctrl_ssao = getChild<LLCheckBoxCtrl>("UseSSAO");
+	LLCheckBoxCtrl* ctrl_dof = getChild<LLCheckBoxCtrl>("UseDoF");
 
 	// if vertex shaders off, disable all shader related products
 	if(!LLFeatureManager::getInstance()->isFeatureAvailable("VertexShaderEnable"))
@@ -1092,6 +1101,9 @@ void LLFloaterPreference::disableUnavailableSettings()
 		ctrl_ssao->setEnabled(FALSE);
 		ctrl_ssao->setValue(FALSE);
 
+		ctrl_dof->setEnabled(FALSE);
+		ctrl_dof->setValue(FALSE);
+
 		ctrl_deferred->setEnabled(FALSE);
 		ctrl_deferred->setValue(FALSE);
 	}
@@ -1109,12 +1121,16 @@ void LLFloaterPreference::disableUnavailableSettings()
 		ctrl_ssao->setEnabled(FALSE);
 		ctrl_ssao->setValue(FALSE);
 
+		ctrl_dof->setEnabled(FALSE);
+		ctrl_dof->setValue(FALSE);
+
 		ctrl_deferred->setEnabled(FALSE);
 		ctrl_deferred->setValue(FALSE);
 	}
 
 	// disabled deferred
-	if(!LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred"))
+	if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") ||
+		!gGLManager.mHasFramebufferObject)
 	{
 		ctrl_shadows->setEnabled(FALSE);
 		ctrl_shadows->setValue(0);
@@ -1122,6 +1138,9 @@ void LLFloaterPreference::disableUnavailableSettings()
 		ctrl_ssao->setEnabled(FALSE);
 		ctrl_ssao->setValue(FALSE);
 
+		ctrl_dof->setEnabled(FALSE);
+		ctrl_dof->setValue(FALSE);
+
 		ctrl_deferred->setEnabled(FALSE);
 		ctrl_deferred->setValue(FALSE);
 	}
@@ -1163,6 +1182,9 @@ void LLFloaterPreference::disableUnavailableSettings()
 		ctrl_ssao->setEnabled(FALSE);
 		ctrl_ssao->setValue(FALSE);
 
+		ctrl_dof->setEnabled(FALSE);
+		ctrl_dof->setValue(FALSE);
+
 		ctrl_deferred->setEnabled(FALSE);
 		ctrl_deferred->setValue(FALSE);
 	}
diff --git a/indra/newview/llfloaterregiondebugconsole.cpp b/indra/newview/llfloaterregiondebugconsole.cpp
index ada0dcf5691f008529761f3ae567e8ce0fe5b257..c7fab2573f29dfffaa97471fbad3059ec4b18e93 100644
--- a/indra/newview/llfloaterregiondebugconsole.cpp
+++ b/indra/newview/llfloaterregiondebugconsole.cpp
@@ -58,8 +58,6 @@ namespace
 {
 	// Signal used to notify the floater of responses from the asynchronous
 	// API.
-	typedef boost::signals2::signal<
-		void (const std::string& output)> console_reply_signal_t;
 	console_reply_signal_t sConsoleReplySignal;
 
 	const std::string PROMPT("\n\n> ");
@@ -132,6 +130,11 @@ namespace
 	};
 }
 
+boost::signals2::connection LLFloaterRegionDebugConsole::setConsoleReplyCallback(const console_reply_signal_t::slot_type& cb)
+{
+	return sConsoleReplySignal.connect(cb);
+}
+
 LLFloaterRegionDebugConsole::LLFloaterRegionDebugConsole(LLSD const & key)
 : LLFloater(key), mOutput(NULL)
 {
diff --git a/indra/newview/llfloaterregiondebugconsole.h b/indra/newview/llfloaterregiondebugconsole.h
index 3aa525724efd36b89597a63263e246b1750bbaa6..fd3af4152e94873d083257bf58a31b6de7e1893a 100644
--- a/indra/newview/llfloaterregiondebugconsole.h
+++ b/indra/newview/llfloaterregiondebugconsole.h
@@ -35,6 +35,9 @@
 
 class LLTextEditor;
 
+typedef boost::signals2::signal<
+	void (const std::string& output)> console_reply_signal_t;
+
 class LLFloaterRegionDebugConsole : public LLFloater, public LLHTTPClient::Responder
 {
 public:
@@ -48,6 +51,8 @@ class LLFloaterRegionDebugConsole : public LLFloater, public LLHTTPClient::Respo
 
 	LLTextEditor * mOutput;
 
+	static boost::signals2::connection setConsoleReplyCallback(const console_reply_signal_t::slot_type& cb);
+
  private:
 	void onReplyReceived(const std::string& output);
 
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 7792b3fb40932cac3553781e9f96856843fb90c1..34fda493754dda653f2fa98e4dbed3c6374d2e0c 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -53,6 +53,7 @@
 #include "llfloatertopobjects.h" // added to fix SL-32336
 #include "llfloatergroups.h"
 #include "llfloaterreg.h"
+#include "llfloaterregiondebugconsole.h"
 #include "llfloatertelehub.h"
 #include "llfloaterwindlight.h"
 #include "llinventorymodel.h"
@@ -159,9 +160,30 @@ bool estate_dispatch_initialized = false;
 //S32 LLFloaterRegionInfo::sRequestSerial = 0;
 LLUUID LLFloaterRegionInfo::sRequestInvoice;
 
+
+void LLFloaterRegionInfo::onConsoleReplyReceived(const std::string& output)
+{
+	llwarns << "here is what they're giving us:  " << output << llendl;
+
+	if (output.find("FALSE") != std::string::npos)
+	{
+		getChild<LLUICtrl>("mesh_rez_enabled_check")->setValue(FALSE);
+	}
+	else
+	{
+		getChild<LLUICtrl>("mesh_rez_enabled_check")->setValue(TRUE);
+	}
+}
+
+
 LLFloaterRegionInfo::LLFloaterRegionInfo(const LLSD& seed)
 	: LLFloater(seed)
 {
+	mConsoleReplySignalConnection = LLFloaterRegionDebugConsole::setConsoleReplyCallback(
+	boost::bind(
+		&LLFloaterRegionInfo::onConsoleReplyReceived,
+		this,
+		_1));
 }
 
 BOOL LLFloaterRegionInfo::postBuild()
@@ -211,12 +233,14 @@ BOOL LLFloaterRegionInfo::postBuild()
 
 LLFloaterRegionInfo::~LLFloaterRegionInfo()
 {
+	mConsoleReplySignalConnection.disconnect();
 }
 
 void LLFloaterRegionInfo::onOpen(const LLSD& key)
 {
 	refreshFromRegion(gAgent.getRegion());
 	requestRegionInfo();
+	requestMeshRezInfo();
 }
 
 // static
@@ -584,6 +608,7 @@ BOOL LLPanelRegionGeneralInfo::postBuild()
 	initCtrl("access_combo");
 	initCtrl("restrict_pushobject");
 	initCtrl("block_parcel_search_check");
+	initCtrl("mesh_rez_enabled_check");
 
 	childSetAction("kick_btn", boost::bind(&LLPanelRegionGeneralInfo::onClickKick, this));
 	childSetAction("kick_all_btn", onClickKickAll, this);
@@ -691,7 +716,42 @@ bool LLPanelRegionGeneralInfo::onMessageCommit(const LLSD& notification, const L
 	return false;
 }
 
+class ConsoleRequestResponder : public LLHTTPClient::Responder
+{
+public:
+	/*virtual*/
+	void error(U32 status, const std::string& reason)
+	{
+		llwarns << "requesting mesh_rez_enabled failed" << llendl;
+	}
+};
+
+
+// called if this request times out.
+class ConsoleUpdateResponder : public LLHTTPClient::Responder
+{
+public:
+	/* virtual */
+	void error(U32 status, const std::string& reason)
+	{
+		llwarns << "Updating mesh enabled region setting failed" << llendl;
+	}
+};
+
+void LLFloaterRegionInfo::requestMeshRezInfo()
+{
+	std::string sim_console_url = gAgent.getRegion()->getCapability("SimConsoleAsync");
 
+	if (!sim_console_url.empty())
+	{
+		std::string request_str = "get mesh_rez_enabled";
+		
+		LLHTTPClient::post(
+			sim_console_url,
+			LLSD(request_str),
+			new ConsoleRequestResponder);
+	}
+}
 
 // setregioninfo
 // strings[0] = 'Y' - block terraform, 'N' - not
@@ -764,6 +824,27 @@ BOOL LLPanelRegionGeneralInfo::sendUpdate()
 		sendEstateOwnerMessage(gMessageSystem, "setregioninfo", invoice, strings);
 	}
 
+	std::string sim_console_url = gAgent.getRegion()->getCapability("SimConsoleAsync");
+
+	if (!sim_console_url.empty())
+	{
+		std::string update_str = "set mesh_rez_enabled ";
+		if (getChild<LLUICtrl>("mesh_rez_enabled_check")->getValue().asBoolean())
+		{
+			update_str += "true";
+		}
+		else
+		{
+			update_str += "false";
+		}
+
+		LLHTTPClient::post(
+			sim_console_url,
+			LLSD(update_str),
+			new ConsoleUpdateResponder);
+	}
+
+
 	// if we changed access levels, tell user about it
 	LLViewerRegion* region = gAgent.getRegion();
 	if (region && (getChild<LLUICtrl>("access_combo")->getValue().asInteger() != region->getSimAccess()) )
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index c0758fa92d31efb39f990872eb54f6283eee4a4a..2b87c27fcf378ce52e0053e775d85258f19d5360 100644
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -84,11 +84,16 @@ class LLFloaterRegionInfo : public LLFloater
 	virtual void refresh();
 	
 	void requestRegionInfo();
+	void requestMeshRezInfo();
 
 private:
 	
 	LLFloaterRegionInfo(const LLSD& seed);
 	~LLFloaterRegionInfo();
+
+	void onConsoleReplyReceived(const std::string& output);
+
+	boost::signals2::connection mConsoleReplySignalConnection;;
 	
 protected:
 	void refreshFromRegion(LLViewerRegion* region);
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
index 364fbad193ebb0c39242adf2ce546f4f59ae733e..73c1f99fa04df5f167f70e598e2440589fd596a8 100644
--- a/indra/newview/llfloatertools.cpp
+++ b/indra/newview/llfloatertools.cpp
@@ -32,6 +32,7 @@
 #include "llcoord.h"
 //#include "llgl.h"
 
+#include "llagent.h"
 #include "llagentcamera.h"
 #include "llbutton.h"
 #include "llcheckboxctrl.h"
@@ -420,27 +421,83 @@ void LLFloaterTools::refresh()
 
 	// Refresh object and prim count labels
 	LLLocale locale(LLLocale::USER_LOCALE);
-	std::string obj_count_string;
-	LLResMgr::getInstance()->getIntegerString(obj_count_string, LLSelectMgr::getInstance()->getSelection()->getRootObjectCount());
-	getChild<LLUICtrl>("obj_count")->setTextArg("[COUNT]", obj_count_string);	
-	std::string prim_count_string;
-	LLResMgr::getInstance()->getIntegerString(prim_count_string, LLSelectMgr::getInstance()->getSelection()->getObjectCount());
-	getChild<LLUICtrl>("prim_count")->setTextArg("[COUNT]", prim_count_string);
-
-	// calculate selection rendering cost
-	if (sShowObjectCost)
-	{
-		std::string prim_cost_string;
-		LLResMgr::getInstance()->getIntegerString(prim_cost_string, calcRenderCost());
-		getChild<LLUICtrl>("RenderingCost")->setTextArg("[COUNT]", prim_cost_string);
+
+	if ((gAgent.getRegion() && gAgent.getRegion()->getCapability("GetMesh").empty()) || !gSavedSettings.getBOOL("MeshEnabled"))
+	{		
+		std::string obj_count_string;
+		LLResMgr::getInstance()->getIntegerString(obj_count_string, LLSelectMgr::getInstance()->getSelection()->getRootObjectCount());
+		getChild<LLUICtrl>("obj_count")->setTextArg("[COUNT]", obj_count_string);	
+		std::string prim_count_string;
+		LLResMgr::getInstance()->getIntegerString(prim_count_string, LLSelectMgr::getInstance()->getSelection()->getObjectCount());
+		getChild<LLUICtrl>("prim_count")->setTextArg("[COUNT]", prim_count_string);
+
+		// calculate selection rendering cost
+		if (sShowObjectCost)
+		{
+			std::string prim_cost_string;
+			LLResMgr::getInstance()->getIntegerString(prim_cost_string, calcRenderCost());
+			getChild<LLUICtrl>("RenderingCost")->setTextArg("[COUNT]", prim_cost_string);
+		}
+		
+		// disable the object and prim counts if nothing selected
+		bool have_selection = ! LLSelectMgr::getInstance()->getSelection()->isEmpty();
+		getChildView("obj_count")->setEnabled(have_selection);
+		getChildView("prim_count")->setEnabled(have_selection);
+		getChildView("RenderingCost")->setEnabled(have_selection && sShowObjectCost);
 	}
+	else
+	{
+		// Get the number of objects selected
+		std::string root_object_count_string;
+		std::string object_count_string;
+
+		LLResMgr::getInstance()->getIntegerString(
+			root_object_count_string,
+			LLSelectMgr::getInstance()->getSelection()->getRootObjectCount());
+		LLResMgr::getInstance()->getIntegerString(
+			object_count_string,
+			LLSelectMgr::getInstance()->getSelection()->getObjectCount());
+
+		F32 obj_cost =
+			LLSelectMgr::getInstance()->getSelection()->getSelectedObjectCost();
+		F32 link_cost =
+			LLSelectMgr::getInstance()->getSelection()->getSelectedLinksetCost();
+		F32 obj_physics_cost =
+			LLSelectMgr::getInstance()->getSelection()->getSelectedPhysicsCost();
+		F32 link_physics_cost =
+			LLSelectMgr::getInstance()->getSelection()->getSelectedLinksetPhysicsCost();
+
+		// Update the text for the counts
+		childSetTextArg(
+			"linked_set_count",
+			"[COUNT]",
+			root_object_count_string);
+		childSetTextArg("object_count", "[COUNT]", object_count_string);
+
+		// Update the text for the resource costs
+		childSetTextArg("linked_set_cost","[COST]",llformat("%.1f", link_cost));
+		childSetTextArg("object_cost", "[COST]", llformat("%.1f", obj_cost));
+		childSetTextArg("linked_set_cost","[PHYSICS]",llformat("%.1f", link_physics_cost));
+		childSetTextArg("object_cost", "[PHYSICS]", llformat("%.1f", obj_physics_cost));
+
+		// Display rendering cost if needed
+		if (sShowObjectCost)
+		{
+			std::string prim_cost_string;
+			LLResMgr::getInstance()->getIntegerString(prim_cost_string, calcRenderCost());
+			getChild<LLUICtrl>("RenderingCost")->setTextArg("[COUNT]", prim_cost_string);
+		}
 
 
-	// disable the object and prim counts if nothing selected
-	bool have_selection = ! LLSelectMgr::getInstance()->getSelection()->isEmpty();
-	getChildView("obj_count")->setEnabled(have_selection);
-	getChildView("prim_count")->setEnabled(have_selection);
-	getChildView("RenderingCost")->setEnabled(have_selection && sShowObjectCost);
+		// disable the object and prim counts if nothing selected
+		bool have_selection = ! LLSelectMgr::getInstance()->getSelection()->isEmpty();
+		childSetEnabled("linked_set_count", have_selection);
+		childSetEnabled("object_count", have_selection);
+		childSetEnabled("linked_set_cost", have_selection);
+		childSetEnabled("object_cost", have_selection);
+		getChildView("RenderingCost")->setEnabled(have_selection && sShowObjectCost);
+	}
+
 
 	// Refresh child tabs
 	mPanelPermissions->refresh();
@@ -730,8 +787,18 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
 		getChildView("Strength:")->setVisible( land_visible);
 	}
 
-	getChildView("obj_count")->setVisible( !land_visible);
-	getChildView("prim_count")->setVisible( !land_visible);
+	bool show_mesh_cost = gAgent.getRegion() && 
+		                  !gAgent.getRegion()->getCapability("GetMesh").empty() && 
+						  gSavedSettings.getBOOL("MeshEnabled");
+
+	getChildView("obj_count")->setVisible( !land_visible && !show_mesh_cost);
+	getChildView("prim_count")->setVisible( !land_visible && !show_mesh_cost);
+	getChildView("linked_set_count")->setVisible( !land_visible && show_mesh_cost);
+	getChildView("linked_set_cost")->setVisible( !land_visible && show_mesh_cost);
+	getChildView("object_count")->setVisible( !land_visible && show_mesh_cost);
+	getChildView("object_cost")->setVisible( !land_visible && show_mesh_cost);
+	getChildView("RenderingCost")->setVisible( !land_visible && sShowObjectCost);
+	
 	mTab->setVisible(!land_visible);
 	mPanelLandInfo->setVisible(land_visible);
 }
@@ -988,30 +1055,33 @@ void LLFloaterTools::onClickGridOptions()
 
 S32 LLFloaterTools::calcRenderCost()
 {
-	S32 cost = 0;
-	std::set<LLUUID> textures;
-
-	for (LLObjectSelection::iterator selection_iter = LLSelectMgr::getInstance()->getSelection()->begin();
-		  selection_iter != LLSelectMgr::getInstance()->getSelection()->end();
-		  ++selection_iter)
-	{
-		LLSelectNode *select_node = *selection_iter;
-		if (select_node)
-		{
-			LLVOVolume *viewer_volume = (LLVOVolume*)select_node->getObject();
-			if (viewer_volume)
-			{
-				cost += viewer_volume->getRenderCost(textures);
-				cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
-				textures.clear();
-			}
-		}
-	}
-
-
-	return cost;
+       S32 cost = 0;
+       std::set<LLUUID> textures;
+
+       for (LLObjectSelection::iterator selection_iter = LLSelectMgr::getInstance()->getSelection()->begin();
+                 selection_iter != LLSelectMgr::getInstance()->getSelection()->end();
+                 ++selection_iter)
+       {
+               LLSelectNode *select_node = *selection_iter;
+               if (select_node)
+               {
+                       LLViewerObject *vobj = select_node->getObject();
+                       if (vobj->getVolume())
+                       {
+                               LLVOVolume* volume = (LLVOVolume*) vobj;
+
+                               cost += volume->getRenderCost(textures);
+							   cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
+							   textures.clear();
+                       }
+               }
+       }
+
+
+       return cost;
 }
 
+
 // static
 void LLFloaterTools::setEditTool(void* tool_pointer)
 {
diff --git a/indra/newview/llhudicon.cpp b/indra/newview/llhudicon.cpp
index 568b0ae5854d4a2ff07f0a5830e785f8e145e7be..7e1025c41b4695d1fc195fa70670a6a10ecf5f21 100644
--- a/indra/newview/llhudicon.cpp
+++ b/indra/newview/llhudicon.cpp
@@ -33,6 +33,7 @@
 
 #include "llviewerobject.h"
 #include "lldrawable.h"
+#include "llvector4a.h"
 #include "llviewercamera.h"
 #include "llviewertexture.h"
 #include "llviewerwindow.h"
@@ -255,21 +256,42 @@ BOOL LLHUDIcon::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 	LLVector3 x_scale = image_aspect * (F32)gViewerWindow->getWindowHeightScaled() * mScale * scale_factor * x_pixel_vec;
 	LLVector3 y_scale = (F32)gViewerWindow->getWindowHeightScaled() * mScale * scale_factor * y_pixel_vec;
 
-	LLVector3 lower_left = icon_position - (x_scale * 0.5f);
-	LLVector3 lower_right = icon_position + (x_scale * 0.5f);
-	LLVector3 upper_left = icon_position - (x_scale * 0.5f) + y_scale;
-	LLVector3 upper_right = icon_position + (x_scale * 0.5f) + y_scale;
+	LLVector4a x_scalea;
+	LLVector4a icon_positiona;
+	LLVector4a y_scalea;
 
-	
-	F32 t = 0.f;
-	LLVector3 dir = end-start;
+	x_scalea.load3(x_scale.mV);
+	x_scalea.mul(0.5f);
+	y_scalea.load3(y_scale.mV);
+
+	icon_positiona.load3(icon_position.mV);
+
+	LLVector4a lower_left;
+	lower_left.setSub(icon_positiona, x_scalea);
+	LLVector4a lower_right;
+	lower_right.setAdd(icon_positiona, x_scalea);
+	LLVector4a upper_left;
+	upper_left.setAdd(lower_left, y_scalea);
+	LLVector4a upper_right;
+	upper_right.setAdd(lower_right, y_scalea);
+
+	LLVector4a enda;
+	enda.load3(end.mV);
+	LLVector4a starta;
+	starta.load3(start.mV);
+	LLVector4a dir;
+	dir.setSub(enda, starta);
+
+	F32 a,b,t;
 
-	if (LLTriangleRayIntersect(upper_right, upper_left, lower_right, start, dir, NULL, NULL, &t, FALSE) ||
-		LLTriangleRayIntersect(upper_left, lower_left, lower_right, start, dir, NULL, NULL, &t, FALSE))
+	if (LLTriangleRayIntersect(upper_right, upper_left, lower_right, starta, dir, a,b,t) ||
+		LLTriangleRayIntersect(upper_left, lower_left, lower_right, starta, dir, a,b,t))
 	{
 		if (intersection)
 		{
-			*intersection = start + dir*t;
+			dir.mul(t);
+			starta.add(dir);
+			*intersection = LLVector3(starta.getF32ptr());
 		}
 		return TRUE;
 	}
diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp
index fc758569e4b5fcf6d0ba005dfe2c97bb62db13a8..82e1f2dfb5ed2ae947e830ef714171ce498686df 100644
--- a/indra/newview/llhudnametag.cpp
+++ b/indra/newview/llhudnametag.cpp
@@ -146,24 +146,43 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3&
 
 	mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
 
+	LLVector3 position = mPositionAgent;
+
+	if (mSourceObject)
+	{ //get intersection of eye through mPositionAgent to plane of source object
+		//using this position keeps the camera from focusing on some seemingly random 
+		//point several meters in front of the nametag
+		const LLVector3& p = mSourceObject->getPositionAgent();
+		const LLVector3& n = LLViewerCamera::getInstance()->getAtAxis();
+		const LLVector3& eye = LLViewerCamera::getInstance()->getOrigin();
+
+		LLVector3 ray = position-eye;
+		ray.normalize();
+
+		LLVector3 delta = p-position;
+		F32 dist = delta*n;
+		F32 dt =  dist/(ray*n);
+		position += ray*dt;
+	}
+
 	// scale screen size of borders down
 	//RN: for now, text on hud objects is never occluded
 
 	LLVector3 x_pixel_vec;
 	LLVector3 y_pixel_vec;
 	
-	LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+	LLViewerCamera::getInstance()->getPixelVectors(position, y_pixel_vec, x_pixel_vec);
 
 	LLVector3 width_vec = mWidth * x_pixel_vec;
 	LLVector3 height_vec = mHeight * y_pixel_vec;
 	
 	LLCoordGL screen_pos;
-	LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE);
+	LLViewerCamera::getInstance()->projectPosAgentToScreen(position, screen_pos, FALSE);
 
 	LLVector2 screen_offset;
 	screen_offset = updateScreenPos(mPositionOffset);
 	
-	LLVector3 render_position = mPositionAgent  
+	LLVector3 render_position = position  
 			+ (x_pixel_vec * screen_offset.mV[VX])
 			+ (y_pixel_vec * screen_offset.mV[VY]);
 
@@ -197,10 +216,10 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3&
 		}
 
 		LLVector3 dir = end-start;
-		F32 t = 0.f;
+		F32 a, b, t;
 
-		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, NULL, NULL, &t, FALSE) ||
-			LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, NULL, NULL, &t, FALSE) )
+		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) ||
+			LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) )
 		{
 			if (t <= 1.f)
 			{
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index bdb9f6167a5be741a3bddbb065426725f96a50d4..86c8a1a9b58d16fd543c6c4dba3132be78c48a2b 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -917,6 +917,14 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
 			// Only should happen for broken links.
 			new_listener = new LLLinkItemBridge(inventory, root, uuid);
 			break;
+	    case LLAssetType::AT_MESH:
+			if(!(inv_type == LLInventoryType::IT_MESH))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLMeshBridge(inventory, root, uuid);
+			break;
+
 		default:
 			llinfos << "Unhandled asset type (llassetstorage.h): "
 					<< (S32)asset_type << llendl;
@@ -2266,6 +2274,9 @@ LLUIImagePtr LLFolderBridge::getIcon() const
 LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type)
 {
 	return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE));
+		/*case LLAssetType::AT_MESH:
+			control = "inv_folder_mesh.tga";
+			break;*/
 }
 
 LLUIImagePtr LLFolderBridge::getOpenIcon() const
@@ -2725,12 +2736,13 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
 		case DAD_CALLINGCARD:
 		case DAD_LANDMARK:
 		case DAD_SCRIPT:
+		case DAD_CLOTHING:
 		case DAD_OBJECT:
 		case DAD_NOTECARD:
-		case DAD_CLOTHING:
 		case DAD_BODYPART:
 		case DAD_ANIMATION:
 		case DAD_GESTURE:
+		case DAD_MESH:
 			accept = dragItemIntoFolder(inv_item, drop);
 			break;
 		case DAD_LINK:
@@ -2760,7 +2772,11 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
 				accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop);
 			}
 			break;
+		case DAD_ROOT_CATEGORY:
+		case DAD_NONE:
+			break;
 		default:
+			llwarns << "Unhandled cargo type for drag&drop " << cargo_type << llendl;
 			break;
 	}
 	return accept;
@@ -3752,6 +3768,7 @@ BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,
 			case DAD_BODYPART:
 			case DAD_ANIMATION:
 			case DAD_GESTURE:
+			case DAD_MESH:
 			{
 				LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
 				const LLPermissions& perm = inv_item->getPermissions();
@@ -4928,6 +4945,7 @@ void LLWearableBridge::removeFromAvatar()
 	}
 }
 
+
 // +=================================================+
 // |        LLLinkItemBridge                         |
 // +=================================================+
@@ -4957,6 +4975,63 @@ void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 	hide_context_entries(menu, items, disabled_items);
 }
 
+// +=================================================+
+// |        LLMeshBridge                             |
+// +=================================================+
+
+LLUIImagePtr LLMeshBridge::getIcon() const
+{
+	return LLInventoryIcon::getIcon(LLAssetType::AT_MESH, LLInventoryType::IT_MESH, 0, FALSE);
+}
+
+void LLMeshBridge::openItem()
+{
+	LLViewerInventoryItem* item = getItem();
+	
+	if (item)
+	{
+		// open mesh
+	}
+}
+
+void LLMeshBridge::previewItem()
+{
+	LLViewerInventoryItem* item = getItem();
+	if(item)
+	{
+		// preview mesh
+	}
+}
+
+
+void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+	lldebugs << "LLMeshBridge::buildContextMenu()" << llendl;
+	std::vector<std::string> items;
+	std::vector<std::string> disabled_items;
+
+	if(isItemInTrash())
+	{
+		items.push_back(std::string("Purge Item"));
+		if (!isItemRemovable())
+		{
+			disabled_items.push_back(std::string("Purge Item"));
+		}
+
+		items.push_back(std::string("Restore Item"));
+	}
+	else
+	{
+		items.push_back(std::string("Properties"));
+
+		getClipboardEntries(true, items, disabled_items, flags);
+	}
+
+
+	hide_context_entries(menu, items, disabled_items);
+}
+
+
 // +=================================================+
 // |        LLLinkBridge                             |
 // +=================================================+
@@ -5263,6 +5338,7 @@ class LLWearableBridgeAction: public LLInvFVBridgeAction
 	{
 		wearOnAvatar();
 	}
+
 	virtual ~LLWearableBridgeAction(){}
 protected:
 	LLWearableBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 5ac328dcef2e1d0c303e6b15ba22b3205ed0df16..1e849c88121c32a7802c1f7a6263054105c2936b 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -41,6 +41,7 @@ class LLMenuGL;
 class LLCallingCardObserver;
 class LLViewerJointAttachment;
 
+
 typedef std::vector<std::string> menuentry_vec_t;
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -523,6 +524,24 @@ class LLLinkFolderBridge : public LLItemBridge
 	static std::string sPrefix;
 };
 
+
+class LLMeshBridge : public LLItemBridge
+{
+	friend class LLInvFVBridge;
+public:
+	virtual LLUIImagePtr getIcon() const;
+	virtual void openItem();
+	virtual void previewItem();
+	virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+protected:
+	LLMeshBridge(LLInventoryPanel* inventory, 
+		     LLFolderView* root,
+		     const LLUUID& uuid) :
+                       LLItemBridge(inventory, root, uuid) {}
+};
+
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class LLInvFVBridgeAction
 //
@@ -553,13 +572,23 @@ class LLInvFVBridgeAction
 	LLInventoryModel* mModel;
 };
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Recent Inventory Panel related classes
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLMeshBridgeAction: public LLInvFVBridgeAction
+{
+	friend class LLInvFVBridgeAction;
+public:
+	virtual void	doIt() ;
+	virtual ~LLMeshBridgeAction(){}
+protected:
+	LLMeshBridgeAction(const LLUUID& id,LLInventoryModel* model):LLInvFVBridgeAction(id,model){}
+
+};
+
+
 
 // Overridden version of the Inventory-Folder-View-Bridge for Folders
 class LLRecentItemsFolderBridge : public LLFolderBridge
 {
+	friend class LLInvFVBridgeAction;
 public:
 	// Creates context menu for Folders related to Recent Inventory Panel.
 	// Uses base logic and than removes from visible items "New..." menu items.
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index ba9bea02b9ae26c376bbe9ebe7e1e8b9f1c09c2d..db3b96873043243e5036f8ac91a407cc9313a73b 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -853,3 +853,4 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder)
 		folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
 	}
 }
+
diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp
index 95dea219a8e9c52e6eb093880100a8389564b01a..34734d57c5f9fe842f2b91fc7771c5d7b4eedfe2 100644
--- a/indra/newview/llinventoryicon.cpp
+++ b/indra/newview/llinventoryicon.cpp
@@ -86,6 +86,7 @@ LLIconDictionary::LLIconDictionary()
 
 	addEntry(LLInventoryIcon::ICONNAME_LINKITEM, 				new IconEntry("Inv_LinkItem"));
 	addEntry(LLInventoryIcon::ICONNAME_LINKFOLDER, 				new IconEntry("Inv_LinkFolder"));
+	addEntry(LLInventoryIcon::ICONNAME_MESH,	 				new IconEntry("Inv_Mesh"));
 
 	addEntry(LLInventoryIcon::ICONNAME_INVALID, 				new IconEntry("Inv_Invalid"));
 
@@ -159,6 +160,8 @@ const std::string& LLInventoryIcon::getIconName(LLAssetType::EType asset_type,
 		case LLAssetType::AT_OBJECT:
 			idx = ICONNAME_OBJECT;
 			break;
+		case LLAssetType::AT_MESH:
+			idx = ICONNAME_MESH;
 		default:
 			break;
 	}
diff --git a/indra/newview/llinventoryicon.h b/indra/newview/llinventoryicon.h
index 694b56d57275d83308e6a1d521ad0abb287a3c86..c7e2998a20fb37062a3bac6eb74cb7c2bb730732 100644
--- a/indra/newview/llinventoryicon.h
+++ b/indra/newview/llinventoryicon.h
@@ -74,6 +74,7 @@ class LLInventoryIcon
 		
 		ICONNAME_LINKITEM,
 		ICONNAME_LINKFOLDER,
+		ICONNAME_MESH,
 
 		ICONNAME_INVALID,
 		ICONNAME_COUNT,
diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h
index 86e11dff1701ccfd95d4461054219d154f57eff8..b183a2052d7a301fecd8a24d79b3243c91b72588 100644
--- a/indra/newview/llinventoryitemslist.h
+++ b/indra/newview/llinventoryitemslist.h
@@ -38,7 +38,6 @@ class LLViewerInventoryItem;
 
 class LLInventoryItemsList : public LLFlatListViewEx
 {
-	LOG_CLASS(LLInventoryItemsList);
 public:
 	struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
 	{
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index b1975c726193752354e3c2da0bb20cb5af6b8661..b5180854efbe73340b71dd627ba5023b850e0c58 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -631,6 +631,12 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
 		return mask;
 	}
 
+	// We're hiding mesh types
+	if (item->getType() == LLAssetType::AT_MESH)
+	{
+		return mask;
+	}
+
 	LLViewerInventoryItem* old_item = getItem(item->getUUID());
 	LLPointer<LLViewerInventoryItem> new_item;
 	if(old_item)
@@ -1263,6 +1269,12 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
 	//llinfos << "LLInventoryModel::addCategory()" << llendl;
 	if(category)
 	{
+		// We aren't displaying the Meshes folder
+		if (category->mPreferredType == LLFolderType::FT_MESH)
+		{
+			return;
+		}
+
 		// try to localize default names first. See EXT-8319, EXT-7051.
 		category->localizeName();
 
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
index 9cdc09225714958405fcf58b30de9c8a298a40b7..738d82e7320ec7f1ca978fcc076537753a82391d 100644
--- a/indra/newview/llmanipscale.cpp
+++ b/indra/newview/llmanipscale.cpp
@@ -52,6 +52,7 @@
 #include "llui.h"
 #include "llviewercamera.h"
 #include "llviewerobject.h"
+#include "llviewerregion.h"
 #include "llviewerwindow.h"
 #include "llhudrender.h"
 #include "llworld.h"
@@ -85,6 +86,22 @@ const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] =
 };
 
 
+F32 get_default_max_prim_scale(bool is_flora) 
+{
+	// a bit of a hack, but if it's foilage, we don't want to use the
+	// new larger scale which would result in giant trees and grass
+	if (gSavedSettings.getBOOL("MeshEnabled") && 
+		gAgent.getRegion() && 
+		!gAgent.getRegion()->getCapability("GetMesh").empty() &&
+		!is_flora)
+	{
+		return DEFAULT_MAX_PRIM_SCALE;
+	}
+	else
+	{	
+		return DEFAULT_MAX_PRIM_SCALE_NO_MESH;
+	}
+}
 
 // static
 void LLManipScale::setUniform(BOOL b)
@@ -242,7 +259,7 @@ void LLManipScale::render()
 				// range != zero
 				F32 fraction_of_fov = BOX_HANDLE_BASE_SIZE / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
 				F32 apparent_angle = fraction_of_fov * LLViewerCamera::getInstance()->getView();  // radians
-				mBoxHandleSize = fsqrtf(range_squared) * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR;
+				mBoxHandleSize = (F32) sqrtf(range_squared) * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR;
 			}
 			else
 			{
@@ -948,8 +965,8 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 		mInSnapRegime = FALSE;
 	}
 
-	F32 max_scale_factor = DEFAULT_MAX_PRIM_SCALE / MIN_PRIM_SCALE;
-	F32 min_scale_factor = MIN_PRIM_SCALE / DEFAULT_MAX_PRIM_SCALE;
+	F32 max_scale_factor = get_default_max_prim_scale() / MIN_PRIM_SCALE;
+	F32 min_scale_factor = MIN_PRIM_SCALE / get_default_max_prim_scale();
 
 	// find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
 	for (LLObjectSelection::iterator iter = mObjectSelection->begin();
@@ -961,7 +978,7 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 		{
 			const LLVector3& scale = selectNode->mSavedScale;
 
-			F32 cur_max_scale_factor = llmin( DEFAULT_MAX_PRIM_SCALE / scale.mV[VX], DEFAULT_MAX_PRIM_SCALE / scale.mV[VY], DEFAULT_MAX_PRIM_SCALE / scale.mV[VZ] );
+			F32 cur_max_scale_factor = llmin( get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VX], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VY], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VZ] );
 			max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
 
 			F32 cur_min_scale_factor = llmax( MIN_PRIM_SCALE / scale.mV[VX], MIN_PRIM_SCALE / scale.mV[VY], MIN_PRIM_SCALE / scale.mV[VZ] );
@@ -1258,7 +1275,7 @@ void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVecto
 
 			F32 denom = axis * dir_local;
 			F32 desired_delta_size	= is_approx_zero(denom) ? 0.f : (delta_local_mag / denom);  // in meters
-			F32 desired_scale		= llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE);
+			F32 desired_scale		= llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, get_default_max_prim_scale(LLPickInfo::isFlora(cur)));
 			// propagate scale constraint back to position offset
 			desired_delta_size		= desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
 
@@ -1958,7 +1975,7 @@ F32		LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
 			max_extent = bbox_extents.mV[i];
 		}
 	}
-	max_scale_factor = bbox_extents.magVec() * DEFAULT_MAX_PRIM_SCALE / max_extent;
+	max_scale_factor = bbox_extents.magVec() * get_default_max_prim_scale() / max_extent;
 
 	if (getUniform())
 	{
@@ -1973,7 +1990,7 @@ F32		LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
 {
 	LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
 	bbox_extents.abs();
-	F32 min_extent = DEFAULT_MAX_PRIM_SCALE;
+	F32 min_extent = get_default_max_prim_scale();
 	for (U32 i = VX; i <= VZ; i++)
 	{
 		if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)
diff --git a/indra/newview/llmanipscale.h b/indra/newview/llmanipscale.h
index 5559f557c023c80a58e18c885d0c7e55e5edb787..5cb8898fd0a6c7f8fd44bd50847b101157a8e4cc 100644
--- a/indra/newview/llmanipscale.h
+++ b/indra/newview/llmanipscale.h
@@ -39,6 +39,9 @@
 #include "llviewerobject.h"
 #include "llbbox.h"
 
+
+F32 get_default_max_prim_scale(bool is_flora = false);
+
 class LLToolComposite;
 class LLColor4;
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a1eadf4d059b32b9c9de35276ab90af352cf4b0
--- /dev/null
+++ b/indra/newview/llmeshrepository.cpp
@@ -0,0 +1,3807 @@
+/** 
+ * @file llmeshrepository.cpp
+ * @brief Mesh repository implementation.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+#include "llhttpstatuscodes.h"
+#include "llmeshrepository.h"
+
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llbufferstream.h"
+#include "llcurl.h"
+#include "lldatapacker.h"
+#include "llfasttimer.h"
+#include "llfloatermodelpreview.h"
+#include "llfloaterperms.h"
+#include "lleconomy.h"
+#include "llimagej2c.h"
+#include "llhost.h"
+#include "llnotificationsutil.h"
+#include "llsd.h"
+#include "llsdutil_math.h"
+#include "llsdserialize.h"
+#include "llthread.h"
+#include "llvfile.h"
+#include "llviewercontrol.h"
+#include "llviewermenufile.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewertexturelist.h"
+#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "material_codes.h"
+#include "pipeline.h"
+#include "llinventorymodel.h"
+#include "llfoldertype.h"
+
+#ifndef LL_WINDOWS
+#include "netdb.h"
+#endif
+
+#include <queue>
+
+LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update");
+LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh");
+
+LLMeshRepository gMeshRepo;
+
+const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
+
+U32 LLMeshRepository::sBytesReceived = 0;
+U32 LLMeshRepository::sHTTPRequestCount = 0;
+U32 LLMeshRepository::sHTTPRetryCount = 0;
+U32 LLMeshRepository::sCacheBytesRead = 0;
+U32 LLMeshRepository::sCacheBytesWritten = 0;
+U32 LLMeshRepository::sPeakKbps = 0;
+	
+
+const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
+
+std::string header_lod[] = 
+{
+	"lowest_lod",
+	"low_lod",
+	"medium_lod",
+	"high_lod"
+};
+
+
+//get the number of bytes resident in memory for given volume
+U32 get_volume_memory_size(const LLVolume* volume)
+{
+	U32 indices = 0;
+	U32 vertices = 0;
+
+	for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+	{
+		const LLVolumeFace& face = volume->getVolumeFace(i);
+		indices += face.mNumIndices;
+		vertices += face.mNumVertices;
+	}
+
+
+	return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces();
+}
+
+void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, F32 scale = 1.f)
+{
+	res.mPositions.clear();
+	res.mNormals.clear();
+	
+	const F32* v = mesh.mVertexBase;
+
+	if (mesh.mIndexType == LLCDMeshData::INT_16)
+	{
+		U16* idx = (U16*) mesh.mIndexBase;
+		for (S32 j = 0; j < mesh.mNumTriangles; ++j)
+		{ 
+			F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
+			F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
+			F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
+
+			idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes);
+			
+			LLVector3 v0(mp0);
+			LLVector3 v1(mp1);
+			LLVector3 v2(mp2);
+
+			LLVector3 n = (v1-v0)%(v2-v0);
+			n.normalize();
+
+			res.mPositions.push_back(v0*scale);
+			res.mPositions.push_back(v1*scale);
+			res.mPositions.push_back(v2*scale);
+
+			res.mNormals.push_back(n);
+			res.mNormals.push_back(n);
+			res.mNormals.push_back(n);			
+		}
+	}
+	else
+	{
+		U32* idx = (U32*) mesh.mIndexBase;
+		for (S32 j = 0; j < mesh.mNumTriangles; ++j)
+		{ 
+			F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
+			F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
+			F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
+
+			idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes);
+			
+			LLVector3 v0(mp0);
+			LLVector3 v1(mp1);
+			LLVector3 v2(mp2);
+
+			LLVector3 n = (v1-v0)%(v2-v0);
+			n.normalize();
+
+			res.mPositions.push_back(v0*scale);
+			res.mPositions.push_back(v1*scale);
+			res.mPositions.push_back(v2*scale);
+
+			res.mNormals.push_back(n);
+			res.mNormals.push_back(n);
+			res.mNormals.push_back(n);			
+		}
+	}
+}
+
+S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
+S32 LLMeshRepoThread::sActiveLODRequests = 0;
+U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
+
+
+class LLTextureCostResponder : public LLCurl::Responder
+{
+public:
+	LLTextureUploadData mData;
+	LLMeshUploadThread* mThread;
+
+	LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread) 
+		: mData(data), mThread(thread)
+	{
+
+	}
+
+	virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+	{
+		mThread->mPendingConfirmations--;
+		if (isGoodStatus(status))
+		{
+			mThread->priceResult(mData, content);	
+		}
+		else
+		{
+			llwarns << status << ": " << reason << llendl;
+
+			if (mData.mRetries < MAX_TEXTURE_UPLOAD_RETRIES)
+			{
+				llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+			
+				if (status == 499 || status == 500)
+				{
+					mThread->uploadTexture(mData);
+				}
+				else
+				{
+					llerrs << "Unhandled status " << status << llendl;
+				}
+			}
+			else
+			{ 
+				llwarns << "Giving up after " << mData.mRetries << " retries." << llendl;
+			}
+		}
+	}
+};
+
+class LLTextureUploadResponder : public LLCurl::Responder
+{
+public:
+	LLTextureUploadData mData;
+	LLMeshUploadThread* mThread;
+
+	LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
+		: mData(data), mThread(thread)
+	{
+	}
+
+	virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+	{
+		mThread->mPendingUploads--;
+		if (isGoodStatus(status))
+		{
+			mData.mUUID = content["new_asset"].asUUID();
+			gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
+			mThread->onTextureUploaded(mData);
+		}
+		else
+		{
+			llwarns << status << ": " << reason << llendl;
+			llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+			if (status == 404)
+			{
+				mThread->uploadTexture(mData);
+			}
+			else if (status == 499)
+			{
+				mThread->mConfirmedTextureQ.push(mData);
+			}
+			else
+			{
+				llerrs << "Unhandled status " << status << llendl;
+			}
+		}
+	}
+};
+
+class LLMeshCostResponder : public LLCurl::Responder
+{
+public:
+	LLMeshUploadData mData;
+	LLMeshUploadThread* mThread;
+
+	LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread) 
+		: mData(data), mThread(thread)
+	{
+
+	}
+
+	virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+	{
+		mThread->mPendingConfirmations--;
+
+		if (isGoodStatus(status))
+		{
+			mThread->priceResult(mData, content);	
+		}
+		else
+		{
+			llwarns << status << ": " << reason << llendl;			
+			
+			if (status == HTTP_INTERNAL_ERROR)
+			{
+				llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+				mThread->uploadModel(mData);
+			}
+			else if (status == HTTP_BAD_REQUEST)
+			{
+				llwarns << "Status 400 received from server, giving up." << llendl;
+			}
+			else if (status == HTTP_NOT_FOUND)
+			{
+				llwarns <<"Status 404 received, server is disconnected, giving up." << llendl ;
+			}
+			else
+			{
+				llerrs << "Unhandled status " << status << llendl;
+			}
+		}
+	}
+};
+
+class LLMeshUploadResponder : public LLCurl::Responder
+{
+public:
+	LLMeshUploadData mData;
+	LLMeshUploadThread* mThread;
+
+	LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
+		: mData(data), mThread(thread)
+	{
+	}
+
+	virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+	{
+		mThread->mPendingUploads--;
+		if (isGoodStatus(status))
+		{
+			mData.mUUID = content["new_asset"].asUUID();
+			if (mData.mUUID.isNull())
+			{
+				LLSD args;
+				std::string message = content["error"]["message"];
+				std::string identifier = content["error"]["identifier"];
+				std::string invalidity_identifier = content["error"]["invalidity_identifier"];
+
+				args["MESSAGE"] = message;
+				args["IDENTIFIER"] = identifier;
+				args["INVALIDITY_IDENTIFIER"] = invalidity_identifier;
+				args["LABEL"] = mData.mBaseModel->mLabel;
+
+				gMeshRepo.uploadError(args);
+			}
+			else
+			{
+				gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
+				mThread->onModelUploaded(mData);
+			}
+		}
+		else
+		{
+			llwarns << status << ": " << reason << llendl;
+			llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+			if (status == 404)
+			{
+				mThread->uploadModel(mData);
+			}
+			else if (status == 499)
+			{
+				mThread->mConfirmedQ.push(mData);
+			}
+			else if (status != 500)
+			{ //drop internal server errors on the floor, otherwise grab
+				llerrs << "Unhandled status " << status << llendl;
+			}
+		}
+	}
+};
+
+
+class LLMeshHeaderResponder : public LLCurl::Responder
+{
+public:
+	LLVolumeParams mMeshParams;
+	
+	LLMeshHeaderResponder(const LLVolumeParams& mesh_params)
+		: mMeshParams(mesh_params)
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshLODResponder : public LLCurl::Responder
+{
+public:
+	LLVolumeParams mMeshParams;
+	S32 mLOD;
+	U32 mRequestedBytes;
+	U32 mOffset;
+
+	LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes)
+		: mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes)
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshSkinInfoResponder : public LLCurl::Responder
+{
+public:
+	LLUUID mMeshID;
+	U32 mRequestedBytes;
+	U32 mOffset;
+
+	LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size)
+		: mMeshID(id), mRequestedBytes(size), mOffset(offset)
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshDecompositionResponder : public LLCurl::Responder
+{
+public:
+	LLUUID mMeshID;
+	U32 mRequestedBytes;
+	U32 mOffset;
+
+	LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size)
+		: mMeshID(id), mRequestedBytes(size), mOffset(offset)
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshPhysicsShapeResponder : public LLCurl::Responder
+{
+public:
+	LLUUID mMeshID;
+	U32 mRequestedBytes;
+	U32 mOffset;
+
+	LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size)
+		: mMeshID(id), mRequestedBytes(size), mOffset(offset)
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLModelObjectUploadResponder: public LLCurl::Responder
+{
+	LLSD mObjectAsset;
+	LLMeshUploadThread* mThread;
+
+public:
+	LLModelObjectUploadResponder(LLMeshUploadThread* thread, const LLSD& object_asset):
+		mThread(thread),
+		mObjectAsset(object_asset)
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+	{
+		assert_main_thread();
+		
+		llinfos << "completed" << llendl;
+		mThread->mPendingUploads--;
+		mThread->mFinished = true;
+	}
+};
+
+class LLWholeModelFeeResponder: public LLCurl::Responder
+{
+	LLMeshUploadThread* mThread;
+public:
+	LLWholeModelFeeResponder(LLMeshUploadThread* thread):
+		mThread(thread)
+	{
+	}
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+	{
+		assert_main_thread();
+		llinfos << "completed" << llendl;
+		mThread->mPendingUploads--;
+	}
+	
+};
+
+LLMeshRepoThread::LLMeshRepoThread()
+: LLThread("mesh repo", NULL) 
+{ 
+	mWaiting = false;
+	mMutex = new LLMutex(NULL);
+	mHeaderMutex = new LLMutex(NULL);
+	mSignal = new LLCondition(NULL);
+}
+
+LLMeshRepoThread::~LLMeshRepoThread()
+{
+	delete mMutex;
+	mMutex = NULL;
+	delete mHeaderMutex;
+	mHeaderMutex = NULL;
+	delete mSignal;
+	mSignal = NULL;
+}
+
+void LLMeshRepoThread::run()
+{
+	mCurlRequest = new LLCurlRequest();
+	LLCDResult res = LLConvexDecomposition::initThread();
+	if (res != LLCD_OK)
+	{
+		llwarns << "convex decomposition unable to be loaded" << llendl;
+	}
+
+	while (!LLApp::isQuitting())
+	{
+		mWaiting = true;
+		mSignal->wait();
+		mWaiting = false;
+
+		if (!LLApp::isQuitting())
+		{
+			static U32 count = 0;
+
+			static F32 last_hundred = gFrameTimeSeconds;
+
+			if (gFrameTimeSeconds - last_hundred > 1.f)
+			{ //a second has gone by, clear count
+				last_hundred = gFrameTimeSeconds;
+				count = 0;	
+			}
+
+			// NOTE: throttling intentionally favors LOD requests over header requests
+			
+			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests)
+			{
+				{
+					mMutex->lock();
+					LODRequest req = mLODReqQ.front();
+					mLODReqQ.pop();
+					mMutex->unlock();
+					if (fetchMeshLOD(req.mMeshParams, req.mLOD))
+					{
+						count++;
+					}
+				}
+			}
+
+			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests)
+			{
+				{
+					mMutex->lock();
+					HeaderRequest req = mHeaderReqQ.front();
+					mHeaderReqQ.pop();
+					mMutex->unlock();
+					if (fetchMeshHeader(req.mMeshParams))
+					{
+						count++;
+					}
+				}
+			}
+
+			{ //mSkinRequests is protected by mSignal
+				std::set<LLUUID> incomplete;
+				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
+				{
+					LLUUID mesh_id = *iter;
+					if (!fetchMeshSkinInfo(mesh_id))
+					{
+						incomplete.insert(mesh_id);
+					}
+				}
+				mSkinRequests = incomplete;
+			}
+
+			{ //mDecompositionRequests is protected by mSignal
+				std::set<LLUUID> incomplete;
+				for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
+				{
+					LLUUID mesh_id = *iter;
+					if (!fetchMeshDecomposition(mesh_id))
+					{
+						incomplete.insert(mesh_id);
+					}
+				}
+				mDecompositionRequests = incomplete;
+			}
+
+			{ //mPhysicsShapeRequests is protected by mSignal
+				std::set<LLUUID> incomplete;
+				for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
+				{
+					LLUUID mesh_id = *iter;
+					if (!fetchMeshPhysicsShape(mesh_id))
+					{
+						incomplete.insert(mesh_id);
+					}
+				}
+				mPhysicsShapeRequests = incomplete;
+			}
+
+			mCurlRequest->process();
+		}
+	}
+	
+	if (mSignal->isLocked())
+	{ //make sure to let go of the mutex associated with the given signal before shutting down
+		mSignal->unlock();
+	}
+
+	res = LLConvexDecomposition::quitThread();
+	if (res != LLCD_OK)
+	{
+		llwarns << "convex decomposition unable to be quit" << llendl;
+	}
+
+	delete mCurlRequest;
+	mCurlRequest = NULL;
+}
+
+void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+	mSkinRequests.insert(mesh_id);
+}
+
+void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+	mDecompositionRequests.insert(mesh_id);
+}
+
+void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+	mPhysicsShapeRequests.insert(mesh_id);
+}
+
+
+void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //protected by mSignal, no locking needed here
+
+	mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+	if (iter != mMeshHeader.end())
+	{ //if we have the header, request LOD byte range
+		LODRequest req(mesh_params, lod);
+		{
+			LLMutexLock lock(mMutex);
+			mLODReqQ.push(req);
+		}
+	}
+	else
+	{ 
+		HeaderRequest req(mesh_params);
+		
+		pending_lod_map::iterator pending = mPendingLOD.find(mesh_params);
+
+		if (pending != mPendingLOD.end())
+		{ //append this lod request to existing header request
+			pending->second.push_back(lod);
+			if (pending->second.size() > 4)
+			{
+				llerrs << "WTF?" << llendl;
+			} 
+		}
+		else
+		{ //if no header request is pending, fetch header
+			LLMutexLock lock(mMutex);
+			mHeaderReqQ.push(req);
+			mPendingLOD[mesh_params].push_back(lod);
+		}
+	}
+}
+
+//static 
+std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
+{
+	std::string http_url;
+	
+	if (gAgent.getRegion())
+	{
+		http_url = gMeshRepo.mGetMeshCapability; 
+	}
+
+	if (!http_url.empty())
+	{
+		http_url += "/?mesh_id=";
+		http_url += mesh_id.asString().c_str();
+	}
+	else
+	{
+		llwarns << "Current region does not have GetMesh capability!  Cannot load " << mesh_id << ".mesh" << llendl;
+	}
+
+	return http_url;
+}
+
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
+{ //protected by mMutex
+	mHeaderMutex->lock();
+
+	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	{ //we have no header info for this mesh, do nothing
+		mHeaderMutex->unlock();
+		return false;
+	}
+
+	U32 header_size = mMeshHeaderSize[mesh_id];
+
+	if (header_size > 0)
+	{
+		S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger();
+		S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger();
+
+		mHeaderMutex->unlock();
+
+		if (offset >= 0 && size > 0)
+		{
+			//check VFS for mesh skin info
+			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+			if (file.getSize() >= offset+size)
+			{
+				LLMeshRepository::sCacheBytesRead += size;
+				file.seek(offset);
+				U8* buffer = new U8[size];
+				file.read(buffer, size);
+
+				//make sure buffer isn't all 0's (reserved block but not written)
+				bool zero = true;
+				for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+				{
+					zero = buffer[i] > 0 ? false : true;
+				}
+
+				if (!zero)
+				{ //attempt to parse
+					if (skinInfoReceived(mesh_id, buffer, size))
+					{
+						delete[] buffer;
+						return true;
+					}
+				}
+
+				delete[] buffer;
+			}
+
+			//reading from VFS failed for whatever reason, fetch from sim
+			std::vector<std::string> headers;
+			headers.push_back("Accept: application/octet-stream");
+
+			std::string http_url = constructUrl(mesh_id);
+			if (!http_url.empty())
+			{
+				++sActiveLODRequests;
+				LLMeshRepository::sHTTPRequestCount++;
+				mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+										   new LLMeshSkinInfoResponder(mesh_id, offset, size));
+			}
+		}
+	}
+	else
+	{	
+		mHeaderMutex->unlock();
+	}
+
+	//early out was not hit, effectively fetched
+	return true;
+}
+
+bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
+{ //protected by mMutex
+	mHeaderMutex->lock();
+
+	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	{ //we have no header info for this mesh, do nothing
+		mHeaderMutex->unlock();
+		return false;
+	}
+
+	U32 header_size = mMeshHeaderSize[mesh_id];
+
+	if (header_size > 0)
+	{
+		S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger();
+		S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger();
+
+		mHeaderMutex->unlock();
+
+		if (offset >= 0 && size > 0)
+		{
+			//check VFS for mesh skin info
+			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+			if (file.getSize() >= offset+size)
+			{
+				LLMeshRepository::sCacheBytesRead += size;
+				file.seek(offset);
+				U8* buffer = new U8[size];
+				file.read(buffer, size);
+
+				//make sure buffer isn't all 0's (reserved block but not written)
+				bool zero = true;
+				for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+				{
+					zero = buffer[i] > 0 ? false : true;
+				}
+
+				if (!zero)
+				{ //attempt to parse
+					if (decompositionReceived(mesh_id, buffer, size))
+					{
+						delete[] buffer;
+						return true;
+					}
+				}
+
+				delete[] buffer;
+			}
+
+			//reading from VFS failed for whatever reason, fetch from sim
+			std::vector<std::string> headers;
+			headers.push_back("Accept: application/octet-stream");
+
+			std::string http_url = constructUrl(mesh_id);
+			if (!http_url.empty())
+			{
+				++sActiveLODRequests;
+				LLMeshRepository::sHTTPRequestCount++;
+				mCurlRequest->getByteRange(http_url, headers, offset, size,
+										   new LLMeshDecompositionResponder(mesh_id, offset, size));
+			}
+		}
+	}
+	else
+	{	
+		mHeaderMutex->unlock();
+	}
+
+	//early out was not hit, effectively fetched
+	return true;
+}
+
+bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
+{ //protected by mMutex
+	mHeaderMutex->lock();
+
+	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	{ //we have no header info for this mesh, do nothing
+		mHeaderMutex->unlock();
+		return false;
+	}
+
+	U32 header_size = mMeshHeaderSize[mesh_id];
+
+	if (header_size > 0)
+	{
+		S32 offset = header_size + mMeshHeader[mesh_id]["physics_shape"]["offset"].asInteger();
+		S32 size = mMeshHeader[mesh_id]["physics_shape"]["size"].asInteger();
+
+		mHeaderMutex->unlock();
+
+		if (offset >= 0 && size > 0)
+		{
+			//check VFS for mesh physics shape info
+			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+			if (file.getSize() >= offset+size)
+			{
+				LLMeshRepository::sCacheBytesRead += size;
+				file.seek(offset);
+				U8* buffer = new U8[size];
+				file.read(buffer, size);
+
+				//make sure buffer isn't all 0's (reserved block but not written)
+				bool zero = true;
+				for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+				{
+					zero = buffer[i] > 0 ? false : true;
+				}
+
+				if (!zero)
+				{ //attempt to parse
+					if (physicsShapeReceived(mesh_id, buffer, size))
+					{
+						delete[] buffer;
+						return true;
+					}
+				}
+
+				delete[] buffer;
+			}
+
+			//reading from VFS failed for whatever reason, fetch from sim
+			std::vector<std::string> headers;
+			headers.push_back("Accept: application/octet-stream");
+
+			std::string http_url = constructUrl(mesh_id);
+			if (!http_url.empty())
+			{
+				++sActiveLODRequests;
+				LLMeshRepository::sHTTPRequestCount++;
+				mCurlRequest->getByteRange(http_url, headers, offset, size,
+										   new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
+			}
+		}
+		else
+		{ //no physics shape whatsoever, report back NULL
+			physicsShapeReceived(mesh_id, NULL, 0);
+		}
+	}
+	else
+	{	
+		mHeaderMutex->unlock();
+	}
+
+	//early out was not hit, effectively fetched
+	return true;
+}
+
+bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
+{
+	bool retval = false;
+
+	{
+		//look for mesh in asset in vfs
+		LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH);
+			
+		S32 size = file.getSize();
+
+		if (size > 0)
+		{
+			U8 buffer[1024];
+			S32 bytes = llmin(size, 1024);
+			LLMeshRepository::sCacheBytesRead += bytes;	
+			file.read(buffer, bytes);
+			if (headerReceived(mesh_params, buffer, bytes))
+			{ //did not do an HTTP request, return false
+				return false;
+			}
+		}
+	}
+
+	//either cache entry doesn't exist or is corrupt, request header from simulator
+
+	std::vector<std::string> headers;
+	headers.push_back("Accept: application/octet-stream");
+
+	std::string http_url = constructUrl(mesh_params.getSculptID());
+	if (!http_url.empty())
+	{
+		++sActiveHeaderRequests;
+		retval = true;
+		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
+		//within the first 4KB
+		LLMeshRepository::sHTTPRequestCount++;
+		mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
+	}
+
+	return retval;
+}
+
+bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //protected by mMutex
+	mHeaderMutex->lock();
+
+	bool retval = false;
+
+	LLUUID mesh_id = mesh_params.getSculptID();
+	
+	U32 header_size = mMeshHeaderSize[mesh_id];
+
+	if (header_size > 0)
+	{
+		S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger();
+		S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger();
+		mHeaderMutex->unlock();
+		if (offset >= 0 && size > 0)
+		{
+
+			//check VFS for mesh asset
+			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+			if (file.getSize() >= offset+size)
+			{
+				LLMeshRepository::sCacheBytesRead += size;
+				file.seek(offset);
+				U8* buffer = new U8[size];
+				file.read(buffer, size);
+
+				//make sure buffer isn't all 0's (reserved block but not written)
+				bool zero = true;
+				for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+				{
+					zero = buffer[i] > 0 ? false : true;
+				}
+
+				if (!zero)
+				{ //attempt to parse
+					if (lodReceived(mesh_params, lod, buffer, size))
+					{
+						delete[] buffer;
+						return false;
+					}
+				}
+
+				delete[] buffer;
+			}
+
+			//reading from VFS failed for whatever reason, fetch from sim
+			std::vector<std::string> headers;
+			headers.push_back("Accept: application/octet-stream");
+
+			std::string http_url = constructUrl(mesh_id);
+			if (!http_url.empty())
+			{
+				++sActiveLODRequests;
+				retval = true;
+				LLMeshRepository::sHTTPRequestCount++;
+				mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+										   new LLMeshLODResponder(mesh_params, lod, offset, size));
+			}
+			else
+			{
+				mUnavailableQ.push(LODRequest(mesh_params, lod));
+			}
+		}
+		else
+		{
+			mUnavailableQ.push(LODRequest(mesh_params, lod));
+		}
+	}
+	else
+	{
+		mHeaderMutex->unlock();
+	}
+
+	return retval;
+}
+
+bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
+{
+	LLSD header;
+	
+	U32 header_size = 0;
+	if (data_size > 0)
+	{
+		std::string res_str((char*) data, data_size);
+
+		std::string deprecated_header("<? LLSD/Binary ?>");
+
+		if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
+		{
+			res_str = res_str.substr(deprecated_header.size()+1, data_size);
+			header_size = deprecated_header.size()+1;
+		}
+		data_size = res_str.size();
+
+		std::istringstream stream(res_str);
+
+		if (!LLSDSerialize::fromBinary(header, stream, data_size))
+		{
+			llwarns << "Mesh header parse error.  Not a valid mesh asset!" << llendl;
+			return false;
+		}
+
+		header_size += stream.tellg();
+	}
+	else
+	{
+		llinfos
+			<< "Marking header as non-existent, will not retry." << llendl;
+		header["404"] = 1;
+	}
+
+	{
+		U32 cost = gMeshRepo.calcResourceCost(header);
+
+		LLUUID mesh_id = mesh_params.getSculptID();
+		
+		mHeaderMutex->lock();
+		mMeshHeaderSize[mesh_id] = header_size;
+		mMeshHeader[mesh_id] = header;
+		mMeshResourceCost[mesh_id] = cost;
+		mHeaderMutex->unlock();
+
+		//check for pending requests
+		pending_lod_map::iterator iter = mPendingLOD.find(mesh_params);
+		if (iter != mPendingLOD.end())
+		{
+			LLMutexLock lock(mMutex);
+			for (U32 i = 0; i < iter->second.size(); ++i)
+			{
+				LODRequest req(mesh_params, iter->second[i]);
+				mLODReqQ.push(req);
+			}
+		}
+		mPendingLOD.erase(iter);
+	}
+
+	return true;
+}
+
+bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size)
+{
+	LLVolume* volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
+	std::string mesh_string((char*) data, data_size);
+	std::istringstream stream(mesh_string);
+
+	if (volume->unpackVolumeFaces(stream, data_size))
+	{
+		LoadedMesh mesh(volume, mesh_params, lod);
+		if (volume->getNumFaces() > 0)
+		{
+			LLMutexLock lock(mMutex);
+			mLoadedQ.push(mesh);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+	LLSD skin;
+
+	if (data_size > 0)
+	{
+		std::string res_str((char*) data, data_size);
+
+		std::istringstream stream(res_str);
+
+		if (!unzip_llsd(skin, stream, data_size))
+		{
+			llwarns << "Mesh skin info parse error.  Not a valid mesh asset!" << llendl;
+			return false;
+		}
+	}
+	
+	{
+		LLMeshSkinInfo info(skin);
+		info.mMeshID = mesh_id;
+
+		//llinfos<<"info pelvis offset"<<info.mPelvisOffset<<llendl;
+		mSkinInfoQ.push(info);
+	}
+
+	return true;
+}
+
+bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+	LLSD decomp;
+
+	if (data_size > 0)
+	{ 
+		std::string res_str((char*) data, data_size);
+
+		std::istringstream stream(res_str);
+
+		if (!unzip_llsd(decomp, stream, data_size))
+		{
+			llwarns << "Mesh decomposition parse error.  Not a valid mesh asset!" << llendl;
+			return false;
+		}
+	}
+	
+	{
+		LLModel::Decomposition* d = new LLModel::Decomposition(decomp);
+		d->mMeshID = mesh_id;
+		mDecompositionQ.push(d);
+	}
+
+	return true;
+}
+
+bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+	LLSD physics_shape;
+
+	LLModel::Decomposition* d = new LLModel::Decomposition();
+	d->mMeshID = mesh_id;
+
+	if (data == NULL)
+	{ //no data, no physics shape exists
+		d->mPhysicsShapeMesh.clear();
+	}
+	else
+	{
+		LLVolumeParams volume_params;
+		volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+		volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH);
+		LLPointer<LLVolume> volume = new LLVolume(volume_params,0);
+		std::string mesh_string((char*) data, data_size);
+		std::istringstream stream(mesh_string);
+
+		if (volume->unpackVolumeFaces(stream, data_size))
+		{
+			//load volume faces into decomposition buffer
+			S32 vertex_count = 0;
+			S32 index_count = 0;
+
+			for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+			{
+				const LLVolumeFace& face = volume->getVolumeFace(i);
+				vertex_count += face.mNumVertices;
+				index_count += face.mNumIndices;
+			}
+
+			d->mPhysicsShapeMesh.clear();
+
+			std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions;
+			std::vector<LLVector3>& norm = d->mPhysicsShapeMesh.mNormals;
+
+			for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+			{
+				const LLVolumeFace& face = volume->getVolumeFace(i);
+			
+				for (S32 i = 0; i < face.mNumIndices; ++i)
+				{
+					U16 idx = face.mIndices[i];
+
+					pos.push_back(LLVector3(face.mPositions[idx].getF32ptr()));
+					norm.push_back(LLVector3(face.mNormals[idx].getF32ptr()));				
+				}			
+			}
+		}
+	}
+
+	mDecompositionQ.push(d);
+	return true;
+}
+
+LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
+										bool upload_skin, bool upload_joints)
+: LLThread("mesh upload"),
+	mDiscarded(FALSE)
+{
+	mInstanceList = data;
+	mUploadTextures = upload_textures;
+	mUploadSkin = upload_skin;
+	mUploadJoints = upload_joints;
+	mMutex = new LLMutex(NULL);
+	mCurlRequest = NULL;
+	mPendingConfirmations = 0;
+	mPendingUploads = 0;
+	mPendingCost = 0;
+	mFinished = false;
+	mOrigin = gAgent.getPositionAgent();
+	mHost = gAgent.getRegionHost();
+	
+	mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset");
+	mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice");
+	mWholeModelUploadCapability = gAgent.getRegion()->getCapability("NewFileAgentInventory");
+
+	mOrigin += gAgent.getAtAxis() * scale.magVec();
+}
+
+LLMeshUploadThread::~LLMeshUploadThread()
+{
+
+}
+
+LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
+{
+	mStage = "single_hull";
+	mModel = mdl;
+	mDecompID = &mdl->mDecompID;
+	mBaseModel = base_model;
+	mThread = thread;
+	
+	//copy out positions and indices
+	if (mdl)
+	{
+		U16 index_offset = 0;
+
+		mPositions.clear();
+		mIndices.clear();
+			
+		//queue up vertex positions and indices
+		for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+		{
+			const LLVolumeFace& face = mdl->getVolumeFace(i);
+			if (mPositions.size() + face.mNumVertices > 65535)
+			{
+				continue;
+			}
+
+			for (U32 j = 0; j < face.mNumVertices; ++j)
+			{
+				mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+			}
+
+			for (U32 j = 0; j < face.mNumIndices; ++j)
+			{
+				mIndices.push_back(face.mIndices[j]+index_offset);
+			}
+
+			index_offset += face.mNumVertices;
+		}
+	}
+
+	mThread->mFinalDecomp = this;
+	mThread->mPhysicsComplete = false;
+}
+
+void LLMeshUploadThread::DecompRequest::completed()
+{
+	if (mThread->mFinalDecomp == this)
+	{
+		mThread->mPhysicsComplete = true;
+	}
+
+	if (mHull.size() != 1)
+	{
+		llerrs << "WTF?" << llendl;
+	}
+
+	mThread->mHullMap[mBaseModel] = mHull[0];
+}
+
+//called in the main thread.
+void LLMeshUploadThread::preStart()
+{
+	//build map of LLModel refs to instances for callbacks
+	for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
+	{
+		mInstance[iter->mModel].push_back(*iter);
+	}
+}
+
+void LLMeshUploadThread::discard()
+{
+	LLMutexLock lock(mMutex) ;
+	mDiscarded = TRUE ;
+}
+
+BOOL LLMeshUploadThread::isDiscarded()
+{
+	LLMutexLock lock(mMutex) ;
+	return mDiscarded ;
+}
+
+void LLMeshUploadThread::run()
+{
+	if (gSavedSettings.getBOOL("MeshUseWholeModelUpload"))
+	{
+		doWholeModelUpload();
+	}
+	else
+	{
+		doIterativeUpload();
+	}
+}
+
+#if 0
+void dumpLLSDToFile(LLSD& content, std::string& filename)
+{
+	std::ofstream of(filename);
+	LLSDSerialize::toPrettyXML(content,of);
+}
+#endif
+
+void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
+{
+	// TODO where do textures go?
+	
+	LLSD result;
+
+	result["folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT);
+	result["asset_type"] = "mesh";
+	result["inventory_type"] = "object";
+	result["name"] = "your name here";
+	result["description"] = "your description here";
+
+	// TODO "optional" fields from the spec
+	
+	LLSD res;
+	res["mesh_list"] = LLSD::emptyArray();
+	res["texture_list"] = LLSD::emptyArray();
+	S32 mesh_num = 0;
+	S32 texture_num = 0;
+	
+	std::set<LLViewerTexture* > textures;
+
+	for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+	{
+		LLMeshUploadData data;
+		data.mBaseModel = iter->first;
+
+		LLModelInstance& instance = *(iter->second.begin());
+
+		for (S32 i = 0; i < 5; i++)
+		{
+			data.mModel[i] = instance.mLOD[i];
+		}
+
+		std::stringstream ostr;
+
+		LLModel::Decomposition& decomp =
+			data.mModel[LLModel::LOD_PHYSICS].notNull() ? 
+			data.mModel[LLModel::LOD_PHYSICS]->mPhysics : 
+			data.mBaseModel->mPhysics;
+
+		decomp.mBaseHull = mHullMap[data.mBaseModel];
+
+		LLSD mesh_header = LLModel::writeModel(
+			ostr,  
+			data.mModel[LLModel::LOD_PHYSICS],
+			data.mModel[LLModel::LOD_HIGH],
+			data.mModel[LLModel::LOD_MEDIUM],
+			data.mModel[LLModel::LOD_LOW],
+			data.mModel[LLModel::LOD_IMPOSTOR], 
+			decomp,
+			mUploadSkin,
+			mUploadJoints);
+
+		data.mAssetData = ostr.str();
+
+		LLSD mesh_entry;
+
+		LLVector3 pos, scale;
+		LLQuaternion rot;
+		LLMatrix4 transformation = instance.mTransform;
+		decomposeMeshMatrix(transformation,pos,rot,scale);
+		
+		mesh_entry["childpos"] = ll_sd_from_vector3(pos);
+		mesh_entry["childrot"] = ll_sd_from_quaternion(rot);
+		mesh_entry["scale"] = ll_sd_from_vector3(scale);
+
+		// TODO should be binary.
+		std::string str = ostr.str();
+		mesh_entry["mesh_data"] = LLSD::Binary(str.begin(),str.end()); 
+
+		res["mesh_list"][mesh_num] = mesh_entry;
+
+		// TODO how do textures in the list map to textures in the meshes?
+		if (mUploadTextures)
+		{
+			for (std::vector<LLImportMaterial>::iterator material_iter = instance.mMaterial.begin();
+				material_iter != instance.mMaterial.end(); ++material_iter)
+			{
+
+				if (textures.find(material_iter->mDiffuseMap.get()) == textures.end())
+				{
+					textures.insert(material_iter->mDiffuseMap.get());
+
+					std::stringstream ostr;
+					if (include_textures) // otherwise data is blank.
+					{
+						LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel);
+						if (!data.mTexture->isRawImageValid())
+						{
+							data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
+						}
+						
+						LLPointer<LLImageJ2C> upload_file =
+							LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
+						ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
+					}
+					LLSD texture_entry;
+					texture_entry["texture_data"] = ostr.str();
+					res["texture_list"][texture_num] = texture_entry;
+					texture_num++;
+				}
+			}
+		}
+
+		mesh_num++;
+	}
+
+	result["asset_resources"] = res;
+#if 0	
+	std::string name("whole_model.xml");
+	dumpLLSDToFile(result,name);
+#endif
+
+	dest = result;
+}
+
+void LLMeshUploadThread::doWholeModelUpload()
+{
+	mCurlRequest = new LLCurlRequest();	
+
+	// Queue up models for hull generation (viewer-side)
+	for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+	{
+		LLMeshUploadData data;
+		data.mBaseModel = iter->first;
+
+		LLModelInstance& instance = *(iter->second.begin());
+
+		for (S32 i = 0; i < 5; i++)
+		{
+			data.mModel[i] = instance.mLOD[i];
+		}
+
+		//queue up models for hull generation
+		LLModel* physics = NULL;
+
+		if (data.mModel[LLModel::LOD_PHYSICS].notNull())
+		{
+			physics = data.mModel[LLModel::LOD_PHYSICS];
+		}
+		else if (data.mModel[LLModel::LOD_MEDIUM].notNull())
+		{
+			physics = data.mModel[LLModel::LOD_MEDIUM];
+		}
+		else
+		{
+			physics = data.mModel[LLModel::LOD_HIGH];
+		}
+
+		if (!physics)
+		{
+			llerrs << "WTF?" << llendl;
+		}
+
+		DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this);
+		gMeshRepo.mDecompThread->submitRequest(request);
+	}
+
+	while (!mPhysicsComplete)
+	{
+		apr_sleep(100);
+	}
+
+	bool do_include_textures = false; // not needed for initial cost/validation check.
+	LLSD model_data;
+	wholeModelToLLSD(model_data, do_include_textures);
+
+	mPendingUploads++;
+	LLCurlRequest::headers_t headers;
+	mCurlRequest->post(mWholeModelUploadCapability, headers, model_data.asString(),
+					   new LLWholeModelFeeResponder(this));
+
+	// Currently a no-op.
+	mFinished = true;
+}
+
+void LLMeshUploadThread::doIterativeUpload()
+{
+	if(isDiscarded())
+	{
+		mFinished = true;
+		return ;
+	}
+	
+	mCurlRequest = new LLCurlRequest();	
+
+	std::set<LLViewerTexture* > textures;
+
+	//populate upload queue with relevant models
+	for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+	{
+		LLMeshUploadData data;
+		data.mBaseModel = iter->first;
+
+		LLModelInstance& instance = *(iter->second.begin());
+
+		for (S32 i = 0; i < 5; i++)
+		{
+			data.mModel[i] = instance.mLOD[i];
+		}
+
+		uploadModel(data);
+
+		if (mUploadTextures)
+		{
+			for (std::vector<LLImportMaterial>::iterator material_iter = instance.mMaterial.begin();
+				material_iter != instance.mMaterial.end(); ++material_iter)
+			{
+
+				if (textures.find(material_iter->mDiffuseMap.get()) == textures.end())
+				{
+					textures.insert(material_iter->mDiffuseMap.get());
+					
+					LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel);
+					uploadTexture(data);
+				}
+			}
+		}
+
+		//queue up models for hull generation
+		DecompRequest* request = new DecompRequest(data.mModel[LLModel::LOD_HIGH], data.mBaseModel, this);
+		gMeshRepo.mDecompThread->submitRequest(request);
+	}
+
+	while (!mPhysicsComplete)
+	{
+		apr_sleep(100);
+	}
+
+	//upload textures
+	bool done = false;
+	do
+	{
+		if (!mTextureQ.empty())
+		{
+			sendCostRequest(mTextureQ.front());
+			mTextureQ.pop();
+		}
+
+		if (!mConfirmedTextureQ.empty())
+		{
+			doUploadTexture(mConfirmedTextureQ.front());
+			mConfirmedTextureQ.pop();
+		}
+
+		mCurlRequest->process();
+
+		done = mTextureQ.empty() && mConfirmedTextureQ.empty();
+	}
+	while (!done || mCurlRequest->getQueued() > 0);
+
+	LLSD object_asset;
+	object_asset["objects"] = LLSD::emptyArray();
+
+	done = false;
+	do 
+	{
+		static S32 count = 0;
+		static F32 last_hundred = gFrameTimeSeconds;
+		if (gFrameTimeSeconds - last_hundred > 1.f)
+		{
+			last_hundred = gFrameTimeSeconds;
+			count = 0;
+		}
+
+		//how many requests to push before calling process
+		const S32 PUSH_PER_PROCESS = 32;
+
+		S32 tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+		while (!mUploadQ.empty() && count < tcount)
+		{ //send any pending upload requests
+			mMutex->lock();
+			LLMeshUploadData data = mUploadQ.front();
+			mUploadQ.pop();
+			mMutex->unlock();
+			sendCostRequest(data);
+			count++;
+		}
+
+		tcount = llmin(count+PUSH_PER_PROCESS, 100);
+		
+		while (!mConfirmedQ.empty() && count < tcount)
+		{ //process any meshes that have been confirmed for upload
+			LLMeshUploadData& data = mConfirmedQ.front();
+			doUploadModel(data);
+			mConfirmedQ.pop();
+			count++;
+		}
+	
+		tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+		while (!mInstanceQ.empty() && count < tcount && !isDiscarded())
+		{ //create any objects waiting for upload
+			count++;
+			object_asset["objects"].append(createObject(mInstanceQ.front()));
+			mInstanceQ.pop();
+		}
+			
+		mCurlRequest->process();
+			
+		done = isDiscarded() || (mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty());
+	}
+	while (!done || mCurlRequest->getQueued() > 0);
+
+	delete mCurlRequest;
+	mCurlRequest = NULL;
+
+	// now upload the object asset
+	std::string url = mUploadObjectAssetCapability;
+
+	if (object_asset["objects"][0].has("permissions"))
+	{ //copy permissions from first available object to be used for coalesced object
+		object_asset["permissions"] = object_asset["objects"][0]["permissions"];
+	}
+
+	if(!isDiscarded())
+	{
+		mPendingUploads++;
+		LLHTTPClient::post(url, object_asset, new LLModelObjectUploadResponder(this,object_asset));
+	}
+	else
+	{
+		mFinished = true;
+	}
+}
+
+void LLMeshUploadThread::uploadModel(LLMeshUploadData& data)
+{ //called from arbitrary thread
+	{
+		LLMutexLock lock(mMutex);
+		mUploadQ.push(data);
+	}
+}
+
+void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data)
+{ //called from mesh upload thread
+	mTextureQ.push(data);	
+}
+
+
+static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded");
+static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable");
+
+void LLMeshRepoThread::notifyLoadedMeshes()
+{
+	while (!mLoadedQ.empty())
+	{
+		mMutex->lock();
+		LoadedMesh mesh = mLoadedQ.front();
+		mLoadedQ.pop();
+		mMutex->unlock();
+		
+		if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0)
+		{
+			gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
+		}
+		else
+		{
+			gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, 
+				LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+		}
+	}
+
+	while (!mUnavailableQ.empty())
+	{
+		mMutex->lock();
+		LODRequest req = mUnavailableQ.front();
+		mUnavailableQ.pop();
+		mMutex->unlock();
+		
+		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+	}
+
+	while (!mSkinInfoQ.empty())
+	{
+		gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front());
+		mSkinInfoQ.pop();
+	}
+
+	while (!mDecompositionQ.empty())
+	{
+		gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front());
+		mDecompositionQ.pop();
+	}
+}
+
+S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) 
+{ //only ever called from main thread
+	LLMutexLock lock(mHeaderMutex);
+	mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+
+	if (iter != mMeshHeader.end())
+	{
+		LLSD& header = iter->second;
+
+		return LLMeshRepository::getActualMeshLOD(header, lod);
+	}
+
+	return lod;
+}
+
+//static
+S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod)
+{
+	lod = llclamp(lod, 0, 3);
+
+	if (header.has("404"))
+	{
+		return -1;
+	}
+
+	if (header[header_lod[lod]]["size"].asInteger() > 0)
+	{
+		return lod;
+	}
+
+	//search down to find the next available lower lod
+	for (S32 i = lod-1; i >= 0; --i)
+	{
+		if (header[header_lod[i]]["size"].asInteger() > 0)
+		{
+			return i;
+		}
+	}
+
+	//search up to find then ext available higher lod
+	for (S32 i = lod+1; i < 4; ++i)
+	{
+		if (header[header_lod[i]]["size"].asInteger() > 0)
+		{
+			return i;
+		}
+	}
+
+	//header exists and no good lod found, treat as 404
+	header["404"] = 1;
+	return -1;
+}
+
+U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id)
+{
+	LLMutexLock lock(mHeaderMutex);
+	
+	std::map<LLUUID, U32>::iterator iter = mMeshResourceCost.find(mesh_id);
+	if (iter != mMeshResourceCost.end())
+	{
+		return iter->second;
+	}
+
+	return 0;
+}
+
+void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
+{
+	mThread->mMeshHeader[data.mUUID] = header;
+
+	// we cache the mesh for default parameters
+	LLVolumeParams volume_params;
+	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+	volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH);
+
+	for (U32 i = 0; i < 4; i++)
+	{
+		if (data.mModel[i].notNull())
+		{
+			LLPointer<LLVolume> volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i));
+			volume->copyVolumeFaces(data.mModel[i]);
+		}
+	}
+
+}
+
+void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+{
+
+	LLMeshRepoThread::sActiveLODRequests--;
+	S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+	if (status < 200 || status > 400)
+	{
+		llwarns << status << ": " << reason << llendl;
+	}
+
+	if (data_size < mRequestedBytes)
+	{
+		if (status == 499 || status == 503)
+		{ //timeout or service unavailable, try again
+			LLMeshRepository::sHTTPRetryCount++;
+			gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
+		}
+		else
+		{
+			llwarns << "Unhandled status " << status << llendl;
+		}
+		return;
+	}
+
+	LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+	U8* data = NULL;
+
+	if (data_size > 0)
+	{
+		data = new U8[data_size];
+		buffer->readAfter(channels.in(), NULL, data, data_size);
+	}
+
+	if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
+	{
+		//good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			file.seek(offset);
+			file.write(data, size);
+			LLMeshRepository::sCacheBytesWritten += size;
+		}
+	}
+
+	delete [] data;
+}
+
+void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+{
+	S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+	if (status < 200 || status > 400)
+	{
+		llwarns << status << ": " << reason << llendl;
+	}
+
+	if (data_size < mRequestedBytes)
+	{
+		if (status == 499 || status == 503)
+		{ //timeout or service unavailable, try again
+			LLMeshRepository::sHTTPRetryCount++;
+			gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
+		}
+		else
+		{
+			llwarns << "Unhandled status " << status << llendl;
+		}
+		return;
+	}
+
+	LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+	U8* data = NULL;
+
+	if (data_size > 0)
+	{
+		data = new U8[data_size];
+		buffer->readAfter(channels.in(), NULL, data, data_size);
+	}
+
+	if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
+	{
+		//good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			LLMeshRepository::sCacheBytesWritten += size;
+			file.seek(offset);
+			file.write(data, size);
+		}
+	}
+
+	delete [] data;
+}
+
+void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+{
+	S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+	if (status < 200 || status > 400)
+	{
+		llwarns << status << ": " << reason << llendl;
+	}
+
+	if (data_size < mRequestedBytes)
+	{
+		if (status == 499 || status == 503)
+		{ //timeout or service unavailable, try again
+			LLMeshRepository::sHTTPRetryCount++;
+			gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
+		}
+		else
+		{
+			llwarns << "Unhandled status " << status << llendl;
+		}
+		return;
+	}
+
+	LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+	U8* data = NULL;
+
+	if (data_size > 0)
+	{
+		data = new U8[data_size];
+		buffer->readAfter(channels.in(), NULL, data, data_size);
+	}
+
+	if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
+	{
+		//good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			LLMeshRepository::sCacheBytesWritten += size;
+			file.seek(offset);
+			file.write(data, size);
+		}
+	}
+
+	delete [] data;
+}
+
+void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+{
+	S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+	if (status < 200 || status > 400)
+	{
+		llwarns << status << ": " << reason << llendl;
+	}
+
+	if (data_size < mRequestedBytes)
+	{
+		if (status == 499 || status == 503)
+		{ //timeout or service unavailable, try again
+			LLMeshRepository::sHTTPRetryCount++;
+			gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
+		}
+		else
+		{
+			llwarns << "Unhandled status " << status << llendl;
+		}
+		return;
+	}
+
+	LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+	U8* data = NULL;
+
+	if (data_size > 0)
+	{
+		data = new U8[data_size];
+		buffer->readAfter(channels.in(), NULL, data, data_size);
+	}
+
+	if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
+	{
+		//good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			LLMeshRepository::sCacheBytesWritten += size;
+			file.seek(offset);
+			file.write(data, size);
+		}
+	}
+
+	delete [] data;
+}
+
+void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+{
+	LLMeshRepoThread::sActiveHeaderRequests--;
+	if (status < 200 || status > 400)
+	{
+		//llwarns
+		//	<< "Header responder failed with status: "
+		//	<< status << ": " << reason << llendl;
+
+		// 503 (service unavailable) or 499 (timeout)
+		// can be due to server load and can be retried
+
+		// TODO*: Add maximum retry logic, exponential backoff
+		// and (somewhat more optional than the others) retries
+		// again after some set period of time
+		if (status == 503 || status == 499)
+		{ //retry
+			LLMeshRepository::sHTTPRetryCount++;
+			LLMeshRepoThread::HeaderRequest req(mMeshParams);
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+			gMeshRepo.mThread->mHeaderReqQ.push(req);
+
+			return;
+		}
+	}
+
+	S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+	U8* data = NULL;
+
+	if (data_size > 0)
+	{
+		data = new U8[data_size];
+		buffer->readAfter(channels.in(), NULL, data, data_size);
+	}
+
+	LLMeshRepository::sBytesReceived += llmin(data_size, 4096);
+
+	if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size))
+	{
+		llwarns
+			<< "Unable to parse mesh header: "
+			<< status << ": " << reason << llendl;
+	}
+	else if (data && data_size > 0)
+	{
+		//header was successfully retrieved from sim, cache in vfs
+		LLUUID mesh_id = mMeshParams.getSculptID();
+		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
+
+		std::stringstream str;
+
+		S32 lod_bytes = 0;
+
+		for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
+		{ //figure out how many bytes we'll need to reserve in the file
+			std::string lod_name = header_lod[i];
+			lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
+		}
+		
+		//just in case skin info or decomposition is at the end of the file (which it shouldn't be)
+		lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
+		lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger());
+
+		S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
+		S32 bytes = lod_bytes + header_bytes; 
+
+		
+		//it's possible for the remote asset to have more data than is needed for the local cache
+		//only allocate as much space in the VFS as is needed for the local cache
+		data_size = llmin(data_size, bytes);
+
+		LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);
+		if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
+		{
+			LLMeshRepository::sCacheBytesWritten += data_size;
+
+			file.write((const U8*) data, data_size);
+			
+			//zero out the rest of the file 
+			U8 block[4096];
+			memset(block, 0, 4096);
+
+			while (bytes-file.tell() > 4096)
+			{
+				file.write(block, 4096);
+			}
+
+			S32 remaining = bytes-file.tell();
+
+			if (remaining < 0 || remaining > 4096)
+			{
+				llerrs << "Bad padding of mesh asset cache entry." << llendl;
+			}
+
+			if (remaining > 0)
+			{
+				file.write(block, remaining);
+			}
+		}
+	}
+
+	delete [] data;
+}
+
+
+LLMeshRepository::LLMeshRepository()
+: mMeshMutex(NULL),
+  mMeshThreadCount(0),
+  mThread(NULL)
+{
+
+}
+
+void LLMeshRepository::init()
+{
+	mMeshMutex = new LLMutex(NULL);
+	
+	LLConvexDecomposition::getInstance()->initSystem();
+
+	mDecompThread = new LLPhysicsDecomp();
+	mDecompThread->start();
+
+	while (!mDecompThread->mInited)
+	{ //wait for physics decomp thread to init
+		apr_sleep(100);
+	}
+
+	
+	
+	mThread = new LLMeshRepoThread();
+	mThread->start();
+}
+
+void LLMeshRepository::shutdown()
+{
+	llinfos << "Shutting down mesh repository." << llendl;
+
+	for (U32 i = 0; i < mUploads.size(); ++i)
+	{
+		llinfos << "Discard the pending mesh uploads " << llendl;
+		mUploads[i]->discard() ; //discard the uploading requests.
+	}
+
+	mThread->mSignal->signal();
+	
+	while (!mThread->isStopped())
+	{
+		apr_sleep(10);
+	}
+	delete mThread;
+	mThread = NULL;
+
+	for (U32 i = 0; i < mUploads.size(); ++i)
+	{
+		llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl;
+		while (!mUploads[i]->isStopped())
+		{
+			apr_sleep(10);
+		}
+		delete mUploads[i];
+	}
+
+	mUploads.clear();
+
+	delete mMeshMutex;
+	mMeshMutex = NULL;
+
+	llinfos << "Shutting down decomposition system." << llendl;
+
+	if (mDecompThread)
+	{
+		mDecompThread->shutdown();		
+		delete mDecompThread;
+		mDecompThread = NULL;
+	}
+
+	LLConvexDecomposition::quitSystem();
+}
+
+//called in the main thread.
+S32 LLMeshRepository::update()
+{
+	if(mUploadWaitList.empty())
+	{
+		return 0 ;
+	}
+
+	S32 size = mUploadWaitList.size() ;
+	for (S32 i = 0; i < size; ++i)
+	{
+		mUploads.push_back(mUploadWaitList[i]);
+		mUploadWaitList[i]->preStart() ;
+		mUploadWaitList[i]->start() ;
+	}
+	mUploadWaitList.clear() ;
+
+	return size ;
+}
+
+S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
+{
+	if (detail < 0 || detail > 4)
+	{
+		return detail;
+	}
+
+	LLFastTimer t(FTM_LOAD_MESH); 
+
+	{
+		LLMutexLock lock(mMeshMutex);
+		//add volume to list of loading meshes
+		mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params);
+		if (iter != mLoadingMeshes[detail].end())
+		{ //request pending for this mesh, append volume id to list
+			iter->second.insert(vobj->getID());
+		}
+		else
+		{
+			//first request for this mesh
+			mLoadingMeshes[detail][mesh_params].insert(vobj->getID());
+			mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
+		}
+	}
+
+	//do a quick search to see if we can't display something while we wait for this mesh to load
+	LLVolume* volume = vobj->getVolume();
+
+	if (volume)
+	{
+		if (volume->getNumVolumeFaces() == 0 && !volume->isTetrahedron())
+		{
+			volume->makeTetrahedron();
+		}
+
+		LLVolumeParams params = volume->getParams();
+
+		LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params);
+
+		if (group)
+		{
+			//first, see if last_lod is available (don't transition down to avoid funny popping a la SH-641)
+			if (last_lod >= 0)
+			{
+				LLVolume* lod = group->refLOD(last_lod);
+				if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+				{
+					group->derefLOD(lod);
+					return last_lod;
+				}
+				group->derefLOD(lod);
+			}
+
+			//next, see what the next lowest LOD available might be
+			for (S32 i = detail-1; i >= 0; --i)
+			{
+				LLVolume* lod = group->refLOD(i);
+				if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+				{
+					group->derefLOD(lod);
+					return i;
+				}
+
+				group->derefLOD(lod);
+			}
+
+			//no lower LOD is a available, is a higher lod available?
+			for (S32 i = detail+1; i < 4; ++i)
+			{
+				LLVolume* lod = group->refLOD(i);
+				if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+				{
+					group->derefLOD(lod);
+					return i;
+				}
+
+				group->derefLOD(lod);
+			}
+		}
+		else
+		{
+			llerrs << "WTF?" << llendl;
+		}
+	}
+
+	return detail;
+}
+
+static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread");
+static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD");
+static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1");
+static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2");
+
+void LLMeshRepository::notifyLoadedMeshes()
+{ //called from main thread
+
+	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
+
+	//clean up completed upload threads
+	for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
+	{
+		LLMeshUploadThread* thread = *iter;
+
+		if (thread->isStopped() && thread->finished())
+		{
+			iter = mUploads.erase(iter);
+			delete thread;
+		}
+		else
+		{
+			++iter;
+		}
+	}
+
+	//update inventory
+	if (!mInventoryQ.empty())
+	{
+		LLMutexLock lock(mMeshMutex);
+		while (!mInventoryQ.empty())
+		{
+			inventory_data& data = mInventoryQ.front();
+
+			LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString());
+			LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString());
+
+			on_new_single_inventory_upload_complete(
+				asset_type,
+				inventory_type,
+				data.mPostData["asset_type"].asString(),
+				data.mPostData["folder_id"].asUUID(),
+				data.mPostData["name"],
+				data.mPostData["description"],
+				data.mResponse,
+				0);
+			
+			mInventoryQ.pop();
+		}
+	}
+
+	//call completed callbacks on finished decompositions
+	mDecompThread->notifyCompleted();
+	
+	if (!mThread->mWaiting)
+	{ //curl thread is churning, wait for it to go idle
+		return;
+	}
+
+	static std::string region_name("never name a region this");
+
+	if (gAgent.getRegion())
+	{ //update capability url 
+		if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
+		{
+			region_name = gAgent.getRegion()->getName();
+		
+			mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
+		}
+	}
+
+	LLFastTimer t(FTM_MESH_UPDATE);
+
+	{
+		LLFastTimer t(FTM_MESH_LOCK1);
+		mMeshMutex->lock();	
+	}
+
+	{
+		LLFastTimer t(FTM_MESH_LOCK2);
+		mThread->mMutex->lock();
+	}
+	
+	//popup queued error messages from background threads
+	while (!mUploadErrorQ.empty())
+	{
+		LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front());
+		mUploadErrorQ.pop();
+	}
+
+	S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests);
+
+	if (push_count > 0)
+	{
+		//calculate "score" for pending requests
+
+		//create score map
+		std::map<LLUUID, F32> score_map;
+
+		for (U32 i = 0; i < 4; ++i)
+		{
+			for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
+			{
+				F32 max_score = 0.f;
+				for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+				{
+					LLViewerObject* object = gObjectList.findObject(*obj_iter);
+
+					if (object)
+					{
+						LLDrawable* drawable = object->mDrawable;
+						if (drawable)
+						{
+							F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
+							max_score = llmax(max_score, cur_score);
+						}
+					}
+				}
+				
+				score_map[iter->first.getSculptID()] = max_score;
+			}
+		}
+
+		//set "score" for pending requests
+		for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
+		{
+			iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+		}
+
+		//sort by "score"
+		std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+
+		while (!mPendingRequests.empty() && push_count > 0)
+		{
+			LLFastTimer t(FTM_LOAD_MESH_LOD);
+			LLMeshRepoThread::LODRequest& request = mPendingRequests.front();
+			mThread->loadMeshLOD(request.mMeshParams, request.mLOD);
+			mPendingRequests.erase(mPendingRequests.begin());
+			push_count--;
+		}
+	}
+
+	//send skin info requests
+	while (!mPendingSkinRequests.empty())
+	{
+		mThread->loadMeshSkinInfo(mPendingSkinRequests.front());
+		mPendingSkinRequests.pop();
+	}
+	
+	//send decomposition requests
+	while (!mPendingDecompositionRequests.empty())
+	{
+		mThread->loadMeshDecomposition(mPendingDecompositionRequests.front());
+		mPendingDecompositionRequests.pop();
+	}
+	
+	//send physics shapes decomposition requests
+	while (!mPendingPhysicsShapeRequests.empty())
+	{
+		mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front());
+		mPendingPhysicsShapeRequests.pop();
+	}
+	
+	mThread->notifyLoadedMeshes();
+
+	mThread->mMutex->unlock();
+	mMeshMutex->unlock();
+
+	mThread->mSignal->signal();
+}
+
+void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
+{
+	mSkinMap[info.mMeshID] = info;
+	mLoadingSkins.erase(info.mMeshID);
+}
+
+void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp)
+{
+	decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID);
+	if (iter == mDecompositionMap.end())
+	{ //just insert decomp into map
+		mDecompositionMap[decomp->mMeshID] = decomp;
+	}
+	else
+	{ //merge decomp with existing entry
+		iter->second->merge(decomp);
+		delete decomp;
+	}
+
+	mLoadingDecompositions.erase(decomp->mMeshID);
+}
+
+void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
+{ //called from main thread
+	S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
+
+	//get list of objects waiting to be notified this mesh is loaded
+	mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params);
+
+	if (volume && obj_iter != mLoadingMeshes[detail].end())
+	{
+		//make sure target volume is still valid
+		if (volume->getNumVolumeFaces() <= 0)
+		{
+			llwarns << "Mesh loading returned empty volume." << llendl;
+			volume->makeTetrahedron();
+		}
+		
+		{ //update system volume
+			LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);
+			if (sys_volume)
+			{
+				sys_volume->copyVolumeFaces(volume);
+				LLPrimitive::getVolumeManager()->unrefVolume(sys_volume);
+			}
+			else
+			{
+				llwarns << "Couldn't find system volume for given mesh." << llendl;
+			}
+		}
+
+		//notify waiting LLVOVolume instances that their requested mesh is available
+		for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		{
+			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
+			if (vobj)
+			{
+				vobj->notifyMeshLoaded();
+			}
+		}
+		
+		mLoadingMeshes[detail].erase(mesh_params);
+	}
+}
+
+void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
+{ //called from main thread
+	//get list of objects waiting to be notified this mesh is loaded
+	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
+
+	F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+
+	if (obj_iter != mLoadingMeshes[lod].end())
+	{
+		for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		{
+			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
+			if (vobj)
+			{
+				LLVolume* obj_volume = vobj->getVolume();
+
+				if (obj_volume && 
+					obj_volume->getDetail() == detail &&
+					obj_volume->getParams() == mesh_params)
+				{ //should force volume to find most appropriate LOD
+					vobj->setVolume(obj_volume->getParams(), lod);
+				}
+			}
+		}
+		
+		mLoadingMeshes[lod].erase(mesh_params);
+	}
+}
+
+S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ 
+	return mThread->getActualMeshLOD(mesh_params, lod);
+}
+
+U32 LLMeshRepository::calcResourceCost(LLSD& header)
+{
+	U32 bytes = 0;
+
+	for (U32 i = 0; i < 4; i++)
+	{
+		bytes += header[header_lod[i]]["size"].asInteger();
+	}
+
+	bytes += header["skin"]["size"].asInteger();
+
+	return bytes/4096 + 1;
+}
+
+U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id)
+{
+	return mThread->getResourceCost(mesh_id);
+}
+
+const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id)
+{
+	if (mesh_id.notNull())
+	{
+		skin_map::iterator iter = mSkinMap.find(mesh_id);
+		if (iter != mSkinMap.end())
+		{
+			return &(iter->second);
+		}
+		
+		//no skin info known about given mesh, try to fetch it
+		{
+			LLMutexLock lock(mMeshMutex);
+			//add volume to list of loading meshes
+			std::set<LLUUID>::iterator iter = mLoadingSkins.find(mesh_id);
+			if (iter == mLoadingSkins.end())
+			{ //no request pending for this skin info
+				mLoadingSkins.insert(mesh_id);
+				mPendingSkinRequests.push(mesh_id);
+			}
+		}
+	}
+
+	return NULL;
+}
+
+void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
+{
+	if (mesh_id.notNull())
+	{
+		LLModel::Decomposition* decomp = NULL;
+		decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
+		if (iter != mDecompositionMap.end())
+		{
+			decomp = iter->second;
+		}
+		
+		//decomposition block hasn't been fetched yet
+		if (!decomp || decomp->mPhysicsShapeMesh.empty())
+		{
+			LLMutexLock lock(mMeshMutex);
+			//add volume to list of loading meshes
+			std::set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);
+			if (iter == mLoadingPhysicsShapes.end())
+			{ //no request pending for this skin info
+				mLoadingPhysicsShapes.insert(mesh_id);
+				mPendingPhysicsShapeRequests.push(mesh_id);
+			}
+		}
+	}
+
+}
+
+LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
+{
+	LLModel::Decomposition* ret = NULL;
+
+	if (mesh_id.notNull())
+	{
+		decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
+		if (iter != mDecompositionMap.end())
+		{
+			ret = iter->second;
+		}
+		
+		//decomposition block hasn't been fetched yet
+		if (!ret || ret->mBaseHullMesh.empty())
+		{
+			LLMutexLock lock(mMeshMutex);
+			//add volume to list of loading meshes
+			std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id);
+			if (iter == mLoadingDecompositions.end())
+			{ //no request pending for this skin info
+				mLoadingDecompositions.insert(mesh_id);
+				mPendingDecompositionRequests.push(mesh_id);
+			}
+		}
+	}
+
+	return ret;
+}
+
+void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail)
+{
+	LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail);
+
+	if (!volume->mHullPoints)
+	{
+		//all default params
+		//execute first stage
+		//set simplify mode to retain
+		//set retain percentage to zero
+		//run second stage
+	}
+
+	LLPrimitive::sVolumeManager->unrefVolume(volume);
+}
+
+bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
+{
+	LLSD mesh = mThread->getMeshHeader(mesh_id);
+	return mesh.has("physics_shape") && mesh["physics_shape"].has("size") && (mesh["physics_shape"]["size"].asInteger() > 0);
+}
+
+LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)
+{
+	return mThread->getMeshHeader(mesh_id);
+}
+
+LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id)
+{
+	static LLSD dummy_ret;
+	if (mesh_id.notNull())
+	{
+		LLMutexLock lock(mHeaderMutex);
+		mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
+		if (iter != mMeshHeader.end())
+		{
+			return iter->second;
+		}
+	}
+
+	return dummy_ret;
+}
+
+
+void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
+									bool upload_skin, bool upload_joints)
+{
+	LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints);
+	mUploadWaitList.push_back(thread);
+}
+
+S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
+{
+	if (mThread)
+	{
+		LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
+		if (iter != mThread->mMeshHeader.end())
+		{
+			LLSD& header = iter->second;
+
+			if (header.has("404"))
+			{
+				return -1;
+			}
+
+			S32 size = header[header_lod[lod]]["size"].asInteger();
+			return size;
+		}
+
+	}
+
+	return -1;
+
+}
+
+void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data)
+{
+	if(isDiscarded())
+	{
+		return ;
+	}
+
+	//write model file to memory buffer
+	std::stringstream ostr;
+
+	LLModel::Decomposition& decomp =
+		data.mModel[LLModel::LOD_PHYSICS].notNull() ? 
+		data.mModel[LLModel::LOD_PHYSICS]->mPhysics : 
+		data.mBaseModel->mPhysics;
+
+	LLSD header = LLModel::writeModel(
+		ostr,
+		data.mModel[LLModel::LOD_PHYSICS],
+		data.mModel[LLModel::LOD_HIGH],
+		data.mModel[LLModel::LOD_MEDIUM],
+		data.mModel[LLModel::LOD_LOW],
+		data.mModel[LLModel::LOD_IMPOSTOR], 
+		decomp,
+		mUploadSkin,
+		mUploadJoints,
+		true);
+
+	std::string desc = data.mBaseModel->mLabel;
+	
+	// Grab the total vertex count of the model
+	// along with other information for the "asset_resources" map
+	// to send to the server.
+	LLSD asset_resources = LLSD::emptyMap();
+
+
+	std::string url = mNewInventoryCapability; 
+
+	if (!url.empty())
+	{
+		LLSD body = generate_new_resource_upload_capability_body(
+			LLAssetType::AT_MESH,
+			desc,
+			desc,
+			LLFolderType::FT_MESH,
+			LLInventoryType::IT_MESH,
+			LLFloaterPerms::getNextOwnerPerms(),
+			LLFloaterPerms::getGroupPerms(),
+			LLFloaterPerms::getEveryonePerms());
+
+		body["asset_resources"] = asset_resources;
+
+		mPendingConfirmations++;
+		LLCurlRequest::headers_t headers;
+
+		data.mPostData = body;
+
+		mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this));
+	}	
+}
+
+void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data)
+{
+	if(isDiscarded())
+	{
+		return ;
+	}
+
+	if (data.mTexture && data.mTexture->getDiscardLevel() >= 0)
+	{
+		LLSD asset_resources = LLSD::emptyMap();
+
+		std::string url = mNewInventoryCapability; 
+
+		if (!url.empty())
+		{
+			LLSD body = generate_new_resource_upload_capability_body(
+				LLAssetType::AT_TEXTURE,
+				data.mLabel,
+				data.mLabel,
+				LLFolderType::FT_TEXTURE,
+				LLInventoryType::IT_TEXTURE,
+				LLFloaterPerms::getNextOwnerPerms(),
+				LLFloaterPerms::getGroupPerms(),
+				LLFloaterPerms::getEveryonePerms());
+
+			body["asset_resources"] = asset_resources;
+
+			mPendingConfirmations++;
+			LLCurlRequest::headers_t headers;
+			
+			data.mPostData = body;
+			mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this));
+		}	
+	}
+}
+
+
+void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data)
+{
+	if(isDiscarded())
+	{
+		return ;
+	}
+
+	if (!data.mRSVP.empty())
+	{
+		std::stringstream ostr;
+
+		LLModel::Decomposition& decomp =
+			data.mModel[LLModel::LOD_PHYSICS].notNull() ? 
+			data.mModel[LLModel::LOD_PHYSICS]->mPhysics : 
+			data.mBaseModel->mPhysics;
+
+		decomp.mBaseHull = mHullMap[data.mBaseModel];
+
+		LLModel::writeModel(
+			ostr,  
+			data.mModel[LLModel::LOD_PHYSICS],
+			data.mModel[LLModel::LOD_HIGH],
+			data.mModel[LLModel::LOD_MEDIUM],
+			data.mModel[LLModel::LOD_LOW],
+			data.mModel[LLModel::LOD_IMPOSTOR], 
+			decomp,
+			mUploadSkin,
+			mUploadJoints);
+
+		data.mAssetData = ostr.str();
+
+		LLCurlRequest::headers_t headers;
+		mPendingUploads++;
+
+		mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this));
+	}
+}
+
+void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data)
+{
+	if(isDiscarded())
+	{
+		return ;
+	}
+
+	if (!data.mRSVP.empty())
+	{
+		std::stringstream ostr;
+		
+		if (!data.mTexture->isRawImageValid())
+		{
+			data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
+		}
+
+		LLPointer<LLImageJ2C> upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
+		
+		ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
+
+		data.mAssetData = ostr.str();
+
+		LLCurlRequest::headers_t headers;
+		mPendingUploads++;
+
+		mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this));
+	}
+}
+
+
+void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data)
+{
+	createObjects(data);
+}
+
+void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data)
+{
+	mTextureMap[data.mTexture] = data;
+}
+
+
+void LLMeshUploadThread::createObjects(LLMeshUploadData& data)
+{
+	instance_list& instances = mInstance[data.mBaseModel];
+
+	for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter)
+	{ //create prims that reference given mesh
+		LLModelInstance& instance = *iter;
+		instance.mMeshID = data.mUUID;
+		mInstanceQ.push(instance);
+	}
+}
+
+void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation,
+											 LLVector3& result_pos,
+											 LLQuaternion& result_rot,
+											 LLVector3& result_scale)
+{
+	// check for reflection
+	BOOL reflected = (transformation.determinant() < 0);
+
+	// compute position
+	LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+	// compute scale
+	LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+	LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+	LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+	F32 x_length = x_transformed.normalize();
+	F32 y_length = y_transformed.normalize();
+	F32 z_length = z_transformed.normalize();
+	LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+    // adjust for "reflected" geometry
+	LLVector3 x_transformed_reflected = x_transformed;
+	if (reflected)
+	{
+		x_transformed_reflected *= -1.0;
+	}
+	
+	// compute rotation
+	LLMatrix3 rotation_matrix;
+	rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
+	LLQuaternion quat_rotation = rotation_matrix.quaternion();
+	quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal.  make it so here.
+	LLVector3 euler_rotation;
+	quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
+
+	result_pos = position + mOrigin;
+	result_scale = scale;
+	result_rot = quat_rotation; 
+}
+
+										 
+LLSD LLMeshUploadThread::createObject(LLModelInstance& instance)
+{
+	LLMatrix4 transformation = instance.mTransform;
+
+	if (instance.mMeshID.isNull())
+	{
+		llerrs << "WTF?" << llendl;
+	}
+
+	// check for reflection
+	BOOL reflected = (transformation.determinant() < 0);
+
+	// compute position
+	LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+	// compute scale
+	LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+	LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+	LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+	F32 x_length = x_transformed.normalize();
+	F32 y_length = y_transformed.normalize();
+	F32 z_length = z_transformed.normalize();
+	LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+    // adjust for "reflected" geometry
+	LLVector3 x_transformed_reflected = x_transformed;
+	if (reflected)
+	{
+		x_transformed_reflected *= -1.0;
+	}
+	
+	// compute rotation
+	LLMatrix3 rotation_matrix;
+	rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
+	LLQuaternion quat_rotation = rotation_matrix.quaternion();
+	quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal.  make it so here.
+	LLVector3 euler_rotation;
+	quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
+
+	//
+	// build parameter block to construct this prim
+	//
+	
+	LLSD object_params;
+
+	// create prim
+
+	// set volume params
+	U8 sculpt_type = LL_SCULPT_TYPE_MESH;
+	if (reflected)
+	{
+		sculpt_type |= LL_SCULPT_FLAG_MIRROR;
+	}
+	LLVolumeParams  volume_params;
+	volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
+	volume_params.setBeginAndEndS( 0.f, 1.f );
+	volume_params.setBeginAndEndT( 0.f, 1.f );
+	volume_params.setRatio  ( 1, 1 );
+	volume_params.setShear  ( 0, 0 );
+	volume_params.setSculptID(instance.mMeshID, sculpt_type);
+	object_params["shape"] = volume_params.asLLSD();
+
+	object_params["material"] = LL_MCODE_WOOD;
+
+	object_params["group-id"] = gAgent.getGroupID();
+	object_params["pos"] = ll_sd_from_vector3(position + mOrigin);
+	object_params["rotation"] = ll_sd_from_quaternion(quat_rotation);
+	object_params["scale"] = ll_sd_from_vector3(scale);
+	object_params["name"] = instance.mLabel;
+
+	// load material from dae file
+	object_params["facelist"] = LLSD::emptyArray();
+	for (S32 i = 0; i < instance.mMaterial.size(); i++)
+	{
+		LLTextureEntry te;
+		LLImportMaterial& mat = instance.mMaterial[i];
+
+		te.setColor(mat.mDiffuseColor);
+
+		LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID;
+
+		if (diffuse_id.notNull())
+		{
+			te.setID(diffuse_id);
+		}
+		else
+		{
+			te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture
+		}
+
+		te.setFullbright(mat.mFullbright);
+
+		object_params["facelist"][i] = te.asLLSD();
+	}
+
+	// set extra parameters
+	LLSculptParams sculpt_params;
+	sculpt_params.setSculptTexture(instance.mMeshID);
+	sculpt_params.setSculptType(sculpt_type);
+	U8 buffer[MAX_OBJECT_PARAMS_SIZE+1];
+	LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE);
+	sculpt_params.pack(dp);
+	std::vector<U8> v(dp.getCurrentSize());
+	memcpy(&v[0], buffer, dp.getCurrentSize());
+	LLSD extra_parameter;
+	extra_parameter["extra_parameter"] = sculpt_params.mType;
+	extra_parameter["param_data"] = v;
+	object_params["extra_parameters"].append(extra_parameter);
+
+	LLPermissions perm;
+	perm.setOwnerAndGroup(gAgent.getID(), gAgent.getID(), LLUUID::null, false);
+	perm.setCreator(gAgent.getID());
+
+	perm.initMasks(PERM_ITEM_UNRESTRICTED | PERM_MOVE, //base
+				   PERM_ITEM_UNRESTRICTED | PERM_MOVE, //owner
+				   LLFloaterPerms::getEveryonePerms(),
+				   LLFloaterPerms::getGroupPerms(),
+				   LLFloaterPerms::getNextOwnerPerms());
+		
+	object_params["permissions"] = ll_create_sd_from_permissions(perm);
+
+	object_params["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+
+	return object_params;
+}
+
+void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content)
+{
+	mPendingCost += content["upload_price"].asInteger();
+	data.mRSVP = content["rsvp"].asString();
+
+	mConfirmedQ.push(data);
+}
+
+void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content)
+{
+	mPendingCost += content["upload_price"].asInteger();
+	data.mRSVP = content["rsvp"].asString();
+
+	mConfirmedTextureQ.push(data);
+}
+
+
+bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
+{
+	if (mDiffuseMap != rhs.mDiffuseMap)
+	{
+		return mDiffuseMap < rhs.mDiffuseMap;
+	}
+
+	if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
+	{
+		return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
+	}
+
+	if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
+	{
+		return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
+	}
+
+	if (mDiffuseColor != rhs.mDiffuseColor)
+	{
+		return mDiffuseColor < rhs.mDiffuseColor;
+	}
+
+	return mFullbright < rhs.mFullbright;
+}
+
+
+void LLMeshRepository::updateInventory(inventory_data data)
+{
+	LLMutexLock lock(mMeshMutex);
+	mInventoryQ.push(data);
+}
+
+void LLMeshRepository::uploadError(LLSD& args)
+{
+	LLMutexLock lock(mMeshMutex);
+	mUploadErrorQ.push(args);
+}
+
+//static
+F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod)
+{
+	F32 dlowest = llmin(radius/0.03f, 256.f);
+	F32 dlow = llmin(radius/0.06f, 256.f);
+	F32 dmid = llmin(radius/0.24f, 256.f);
+	
+	F32 bytes_lowest = header["lowest_lod"]["size"].asReal()/1024.f;
+	F32 bytes_low = header["low_lod"]["size"].asReal()/1024.f;
+	F32 bytes_mid = header["medium_lod"]["size"].asReal()/1024.f;
+	F32 bytes_high = header["high_lod"]["size"].asReal()/1024.f;
+
+	if (bytes)
+	{
+		*bytes = 0;
+		*bytes += header["lowest_lod"]["size"].asInteger();
+		*bytes += header["low_lod"]["size"].asInteger();
+		*bytes += header["medium_lod"]["size"].asInteger();
+		*bytes += header["high_lod"]["size"].asInteger();
+	}
+
+
+	if (bytes_visible)
+	{
+		lod = LLMeshRepository::getActualMeshLOD(header, lod);
+		if (lod >= 0 && lod <= 3)
+		{
+			*bytes_visible = header[header_lod[lod]]["size"].asInteger();
+		}
+	}
+
+	if (bytes_high == 0.f)
+	{
+		return 0.f;
+	}
+
+	if (bytes_mid == 0.f)
+	{
+		bytes_mid = bytes_high;
+	}
+
+	if (bytes_low == 0.f)
+	{
+		bytes_low = bytes_mid;
+	}
+
+	if (bytes_lowest == 0.f)
+	{
+		bytes_lowest = bytes_low;
+	}
+
+	F32 max_area = 65536.f;
+	F32 min_area = 1.f;
+
+	F32 high_area = llmin(F_PI*dmid*dmid, max_area);
+	F32 mid_area = llmin(F_PI*dlow*dlow, max_area);
+	F32 low_area = llmin(F_PI*dlowest*dlowest, max_area);
+	F32 lowest_area = max_area;
+
+	lowest_area -= low_area;
+	low_area -= mid_area;
+	mid_area -= high_area;
+
+	high_area = llclamp(high_area, min_area, max_area);
+	mid_area = llclamp(mid_area, min_area, max_area);
+	low_area = llclamp(low_area, min_area, max_area);
+	lowest_area = llclamp(lowest_area, min_area, max_area);
+
+	F32 total_area = high_area + mid_area + low_area + lowest_area;
+	high_area /= total_area;
+	mid_area /= total_area;
+	low_area /= total_area;
+	lowest_area /= total_area;
+
+	F32 weighted_avg = bytes_high*high_area +
+					   bytes_mid*mid_area +
+					   bytes_low*low_area +
+					  bytes_lowest*lowest_area;
+
+	return weighted_avg * gSavedSettings.getF32("MeshStreamingCostScaler");
+}
+
+
+LLPhysicsDecomp::LLPhysicsDecomp()
+: LLThread("Physics Decomp")
+{
+	mInited = false;
+	mQuitting = false;
+	mDone = false;
+
+	mSignal = new LLCondition(NULL);
+	mMutex = new LLMutex(NULL);
+}
+
+LLPhysicsDecomp::~LLPhysicsDecomp()
+{
+	shutdown();
+
+	delete mSignal;
+	mSignal = NULL;
+	delete mMutex;
+	mMutex = NULL;
+}
+
+void LLPhysicsDecomp::shutdown()
+{
+	if (mSignal)
+	{
+		mQuitting = true;
+		mSignal->signal();
+
+		while (!isStopped())
+		{
+			apr_sleep(10);
+		}
+	}
+}
+
+void LLPhysicsDecomp::submitRequest(LLPhysicsDecomp::Request* request)
+{
+	LLMutexLock lock(mMutex);
+	mRequestQ.push(request);
+	mSignal->signal();
+}
+
+//static
+S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2)
+{	
+	if (gMeshRepo.mDecompThread && gMeshRepo.mDecompThread->mCurRequest.notNull())
+	{
+		return gMeshRepo.mDecompThread->mCurRequest->statusCallback(status, p1, p2);
+	}
+
+	return 1;
+}
+
+void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh)
+{
+	mesh.mVertexBase = mCurRequest->mPositions[0].mV;
+	mesh.mVertexStrideBytes = 12;
+	mesh.mNumVertices = mCurRequest->mPositions.size();
+
+	mesh.mIndexType = LLCDMeshData::INT_16;
+	mesh.mIndexBase = &(mCurRequest->mIndices[0]);
+	mesh.mIndexStrideBytes = 6;
+	
+	mesh.mNumTriangles = mCurRequest->mIndices.size()/3;
+
+	LLCDResult ret = LLCD_OK;
+	if (LLConvexDecomposition::getInstance() != NULL)
+	{
+		ret  = LLConvexDecomposition::getInstance()->setMeshData(&mesh);
+	}
+
+	if (ret)
+	{
+		llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl;
+	}
+}
+
+void LLPhysicsDecomp::doDecomposition()
+{
+	LLCDMeshData mesh;
+	S32 stage = mStageID[mCurRequest->mStage];
+
+	//load data intoLLCD
+	if (stage == 0)
+	{
+		setMeshData(mesh);
+	}
+		
+	//build parameter map
+	std::map<std::string, const LLCDParam*> param_map;
+
+	static const LLCDParam* params = NULL;
+	static S32 param_count = 0;
+	if (!params)
+	{
+		param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
+	}
+	
+	for (S32 i = 0; i < param_count; ++i)
+	{
+		param_map[params[i].mName] = params+i;
+	}
+
+	//set parameter values
+	for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter)
+	{
+		const std::string& name = iter->first;
+		const LLSD& value = iter->second;
+
+		const LLCDParam* param = param_map[name];
+
+		if (param == NULL)
+		{ //couldn't find valid parameter
+			continue;
+		}
+
+		U32 ret = LLCD_OK;
+
+		if (param->mType == LLCDParam::LLCD_FLOAT)
+		{
+			ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal());
+		}
+		else if (param->mType == LLCDParam::LLCD_INTEGER ||
+			param->mType == LLCDParam::LLCD_ENUM)
+		{
+			ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger());
+		}
+		else if (param->mType == LLCDParam::LLCD_BOOLEAN)
+		{
+			ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean());
+		}
+
+		if (ret)
+		{
+			llerrs << "WTF?" << llendl;
+		}
+	}
+
+	mCurRequest->setStatusMessage("Executing.");
+
+	LLCDResult ret = LLCD_OK;
+	
+	if (LLConvexDecomposition::getInstance() != NULL)
+	{
+		ret = LLConvexDecomposition::getInstance()->executeStage(stage);
+	}
+
+	if (ret)
+	{
+		llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl;
+		LLMutexLock lock(mMutex);
+
+		mCurRequest->mHull.clear();
+		mCurRequest->mHullMesh.clear();
+
+		mCurRequest->setStatusMessage("FAIL");
+		
+		completeCurrent();
+	}
+	else
+	{
+		mCurRequest->setStatusMessage("Reading results");
+
+		S32 num_hulls =0;
+		if (LLConvexDecomposition::getInstance() != NULL)
+		{
+			num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage);
+		}
+		
+		mMutex->lock();
+		mCurRequest->mHull.clear();
+		mCurRequest->mHull.resize(num_hulls);
+
+		mCurRequest->mHullMesh.clear();
+		mCurRequest->mHullMesh.resize(num_hulls);
+		mMutex->unlock();
+
+		for (S32 i = 0; i < num_hulls; ++i)
+		{
+			std::vector<LLVector3> p;
+			LLCDHull hull;
+			// if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+			LLConvexDecomposition::getInstance()->getHullFromStage(stage, i, &hull);
+
+			const F32* v = hull.mVertexBase;
+
+			for (S32 j = 0; j < hull.mNumVertices; ++j)
+			{
+				LLVector3 vert(v[0], v[1], v[2]); 
+				p.push_back(vert);
+				v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
+			}
+			
+			LLCDMeshData mesh;
+			// if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+			LLConvexDecomposition::getInstance()->getMeshFromStage(stage, i, &mesh);
+
+			get_vertex_buffer_from_mesh(mesh, mCurRequest->mHullMesh[i]);
+			
+			mMutex->lock();
+			mCurRequest->mHull[i] = p;
+			mMutex->unlock();
+		}
+	
+		{
+			LLMutexLock lock(mMutex);
+
+			mCurRequest->setStatusMessage("FAIL");
+			completeCurrent();						
+		}
+	}
+}
+
+void LLPhysicsDecomp::completeCurrent()
+{
+	LLMutexLock lock(mMutex);
+	mCompletedQ.push(mCurRequest);
+	mCurRequest = NULL;
+}
+
+void LLPhysicsDecomp::notifyCompleted()
+{
+	if (!mCompletedQ.empty())
+	{
+		LLMutexLock lock(mMutex);
+		while (!mCompletedQ.empty())
+		{
+			Request* req = mCompletedQ.front();
+			req->completed();
+			mCompletedQ.pop();
+		}
+	}
+}
+
+
+void make_box(LLPhysicsDecomp::Request * request)
+{
+	LLVector3 min,max;
+	min = request->mPositions[0];
+	max = min;
+
+	for (U32 i = 0; i < request->mPositions.size(); ++i)
+	{
+		update_min_max(min, max, request->mPositions[i]);
+	}
+
+	request->mHull.clear();
+	
+	LLModel::hull box;
+	box.push_back(LLVector3(min[0],min[1],min[2]));
+	box.push_back(LLVector3(max[0],min[1],min[2]));
+	box.push_back(LLVector3(min[0],max[1],min[2]));
+	box.push_back(LLVector3(max[0],max[1],min[2]));
+	box.push_back(LLVector3(min[0],min[1],max[2]));
+	box.push_back(LLVector3(max[0],min[1],max[2]));
+	box.push_back(LLVector3(min[0],max[1],max[2]));
+	box.push_back(LLVector3(max[0],max[1],max[2]));
+
+	request->mHull.push_back(box);
+}
+
+
+void LLPhysicsDecomp::doDecompositionSingleHull()
+{
+	LLCDMeshData mesh;
+	
+	setMeshData(mesh);
+			
+	
+	//set all parameters to default
+	std::map<std::string, const LLCDParam*> param_map;
+
+	static const LLCDParam* params = NULL;
+	static S32 param_count = 0;
+
+	if (!params)
+	{
+		param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
+	}
+	
+	LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
+
+	for (S32 i = 0; i < param_count; ++i)
+	{
+		decomp->setParam(params[i].mName, params[i].mDefault.mIntOrEnumValue);
+	}
+
+	const S32 STAGE_DECOMPOSE = mStageID["Decompose"];
+	const S32 STAGE_SIMPLIFY = mStageID["Simplify"];
+	const S32 DECOMP_PREVIEW = 0;
+	const S32 SIMPLIFY_RETAIN = 0;
+	
+	decomp->setParam("Decompose Quality", DECOMP_PREVIEW);
+	decomp->setParam("Simplify Method", SIMPLIFY_RETAIN);
+	decomp->setParam("Retain%", 0.f);
+
+	LLCDResult ret = LLCD_OK;
+	ret = decomp->executeStage(STAGE_DECOMPOSE);
+	
+	if (ret)
+	{
+		llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl;
+		make_box(mCurRequest);
+	}
+	else
+	{
+		ret = decomp->executeStage(STAGE_SIMPLIFY);
+
+		if (ret)
+		{
+			llwarns << "Could not execute simiplification stage when attempting to create single hull." << llendl;
+			make_box(mCurRequest);
+		}
+		else
+		{
+			S32 num_hulls =0;
+			if (LLConvexDecomposition::getInstance() != NULL)
+			{
+				num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(STAGE_SIMPLIFY);
+			}
+			
+			mMutex->lock();
+			mCurRequest->mHull.clear();
+			mCurRequest->mHull.resize(num_hulls);
+			mCurRequest->mHullMesh.clear();
+			mMutex->unlock();
+
+			for (S32 i = 0; i < num_hulls; ++i)
+			{
+				std::vector<LLVector3> p;
+				LLCDHull hull;
+				// if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+				LLConvexDecomposition::getInstance()->getHullFromStage(STAGE_SIMPLIFY, i, &hull);
+
+				const F32* v = hull.mVertexBase;
+
+				for (S32 j = 0; j < hull.mNumVertices; ++j)
+				{
+					LLVector3 vert(v[0], v[1], v[2]); 
+					p.push_back(vert);
+					v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
+				}
+						
+				mMutex->lock();
+				mCurRequest->mHull[i] = p;
+				mMutex->unlock();
+			}
+		}
+	}
+
+
+	{
+		completeCurrent();
+		
+	}
+}
+
+
+void LLPhysicsDecomp::run()
+{
+	LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
+	decomp->initThread();
+	mInited = true;
+
+	static const LLCDStageData* stages = NULL;
+	static S32 num_stages = 0;
+	
+	if (!stages)
+	{
+		num_stages = decomp->getStages(&stages);
+	}
+
+	for (S32 i = 0; i < num_stages; i++)
+	{
+		mStageID[stages[i].mName] = i;
+	}
+
+	while (!mQuitting)
+	{
+		mSignal->wait();
+		while (!mQuitting && !mRequestQ.empty())
+		{
+			{
+				LLMutexLock lock(mMutex);
+				mCurRequest = mRequestQ.front();
+				mRequestQ.pop();
+			}
+
+			S32& id = *(mCurRequest->mDecompID);
+			if (id == -1)
+			{
+				decomp->genDecomposition(id);
+			}
+			decomp->bindDecomposition(id);
+
+			if (mCurRequest->mStage == "single_hull")
+			{
+				doDecompositionSingleHull();
+			}
+			else
+			{
+				doDecomposition();
+			}		
+		}
+	}
+
+	decomp->quitThread();
+	
+	if (mSignal->isLocked())
+	{ //let go of mSignal's associated mutex
+		mSignal->unlock();
+	}
+
+	mDone = true;
+}
+
+void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg)
+{
+	mStatusMessage = msg;
+}
+
+LLModelInstance::LLModelInstance(LLSD& data)
+{
+	mLocalMeshID = data["mesh_id"].asInteger();
+	mLabel = data["label"].asString();
+	mTransform.setValue(data["transform"]);
+
+	for (U32 i = 0; i < data["material"].size(); ++i)
+	{
+		mMaterial.push_back(LLImportMaterial(data["material"][i]));
+	}
+}
+
+
+LLSD LLModelInstance::asLLSD()
+{	
+	LLSD ret;
+
+	ret["mesh_id"] = mModel->mLocalID;
+	ret["label"] = mLabel;
+	ret["transform"] = mTransform.getValue();
+	
+	for (U32 i = 0; i < mMaterial.size(); ++i)
+	{
+		ret["material"][i] = mMaterial[i].asLLSD();
+	}
+
+	return ret;
+}
+
+LLImportMaterial::LLImportMaterial(LLSD& data)
+{
+	mDiffuseMapFilename = data["diffuse"]["filename"].asString();
+	mDiffuseMapLabel = data["diffuse"]["label"].asString();
+	mDiffuseColor.setValue(data["diffuse"]["color"]);
+	mFullbright = data["fullbright"].asBoolean();
+}
+
+
+LLSD LLImportMaterial::asLLSD()
+{
+	LLSD ret;
+
+	ret["diffuse"]["filename"] = mDiffuseMapFilename;
+	ret["diffuse"]["label"] = mDiffuseMapLabel;
+	ret["diffuse"]["color"] = mDiffuseColor.getValue();
+	ret["fullbright"] = mFullbright;
+	
+	return ret;
+}
+
+void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp)
+{
+	decomp.mMesh.resize(decomp.mHull.size());
+
+	for (U32 i = 0; i < decomp.mHull.size(); ++i)
+	{
+		LLCDHull hull;
+		hull.mNumVertices = decomp.mHull[i].size();
+		hull.mVertexBase = decomp.mHull[i][0].mV;
+		hull.mVertexStrideBytes = 12;
+
+		LLCDMeshData mesh;
+		LLCDResult res = LLCD_OK;
+		if (LLConvexDecomposition::getInstance() != NULL)
+		{
+			res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
+		}
+		if (res == LLCD_OK)
+		{
+			get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]);
+		}
+	}
+
+	if (!decomp.mBaseHull.empty() && decomp.mBaseHullMesh.empty())
+	{ //get mesh for base hull
+		LLCDHull hull;
+		hull.mNumVertices = decomp.mBaseHull.size();
+		hull.mVertexBase = decomp.mBaseHull[0].mV;
+		hull.mVertexStrideBytes = 12;
+
+		LLCDMeshData mesh;
+		LLCDResult res = LLCD_OK;
+		if (LLConvexDecomposition::getInstance() != NULL)
+		{
+			res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
+		}
+		if (res == LLCD_OK)
+		{
+			get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh);
+		}
+	}
+}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
new file mode 100644
index 0000000000000000000000000000000000000000..802e3e1aba46f9083e229583db6ae791c3c00003
--- /dev/null
+++ b/indra/newview/llmeshrepository.h
@@ -0,0 +1,551 @@
+/** 
+ * @file llmeshrepository.h
+ * @brief Client-side repository of mesh assets.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_MESH_REPOSITORY_H
+#define LL_MESH_REPOSITORY_H
+
+#include "llassettype.h"
+#include "llmodel.h"
+#include "lluuid.h"
+#include "llviewertexture.h"
+#include "llvolume.h"
+
+#define LLCONVEXDECOMPINTER_STATIC 1
+
+#include "llconvexdecomposition.h"
+
+class LLVOVolume;
+class LLMeshResponder;
+class LLCurlRequest;
+class LLMutex;
+class LLCondition;
+class LLVFS;
+class LLMeshRepository;
+
+class LLMeshUploadData
+{
+public:
+	LLPointer<LLModel> mBaseModel;
+	LLPointer<LLModel> mModel[5];
+	LLUUID mUUID;
+	U32 mRetries;
+	std::string mRSVP;
+	std::string mAssetData;
+	LLSD mPostData;
+
+	LLMeshUploadData()
+	{
+		mRetries = 0;
+	}
+};
+
+class LLTextureUploadData
+{
+public:
+	LLViewerFetchedTexture* mTexture;
+	LLUUID mUUID;
+	std::string mRSVP;
+	std::string mLabel;
+	U32 mRetries;
+	std::string mAssetData;
+	LLSD mPostData;
+
+	LLTextureUploadData()
+	{
+		mRetries = 0;
+	}
+
+	LLTextureUploadData(LLViewerFetchedTexture* texture, std::string& label)
+		: mTexture(texture), mLabel(label)
+	{
+		mRetries = 0;
+	}
+};
+
+class LLImportMaterial
+{
+public:
+	LLPointer<LLViewerFetchedTexture> mDiffuseMap;
+	std::string mDiffuseMapFilename;
+	std::string mDiffuseMapLabel;
+	LLColor4 mDiffuseColor;
+	bool mFullbright;
+
+	bool operator<(const LLImportMaterial &params) const;
+
+	LLImportMaterial() 
+		: mFullbright(false) 
+	{ 
+		mDiffuseColor.set(1,1,1,1);
+	}
+
+	LLImportMaterial(LLSD& data);
+
+	LLSD asLLSD();
+};
+
+class LLModelInstance 
+{
+public:
+	LLPointer<LLModel> mModel;
+	LLPointer<LLModel> mLOD[5];
+	
+	std::string mLabel;
+
+	LLUUID mMeshID;
+	S32 mLocalMeshID;
+
+	LLMatrix4 mTransform;
+	std::vector<LLImportMaterial> mMaterial;
+
+	LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::vector<LLImportMaterial>& materials)
+		: mModel(model), mLabel(label), mTransform(transform), mMaterial(materials)
+	{
+		mLocalMeshID = -1;
+	}
+
+	LLModelInstance(LLSD& data);
+
+	LLSD asLLSD();
+};
+
+class LLPhysicsDecomp : public LLThread
+{
+public:
+
+	typedef std::map<std::string, LLSD> decomp_params;
+
+	class Request : public LLRefCount
+	{
+	public:
+		//input params
+		S32* mDecompID;
+		std::string mStage;
+		std::vector<LLVector3> mPositions;
+		std::vector<U16> mIndices;
+		decomp_params mParams;
+				
+		//output state
+		std::string mStatusMessage;
+		std::vector<LLModel::PhysicsMesh> mHullMesh;
+		LLModel::convex_hull_decomposition mHull;
+		
+		//status message callback, called from decomposition thread
+		virtual S32 statusCallback(const char* status, S32 p1, S32 p2) = 0;
+
+		//completed callback, called from the main thread
+		virtual void completed() = 0;
+
+		virtual void setStatusMessage(const std::string& msg);
+	};
+
+	LLCondition* mSignal;
+	LLMutex* mMutex;
+	
+	bool mInited;
+	bool mQuitting;
+	bool mDone;
+	
+	LLPhysicsDecomp();
+	~LLPhysicsDecomp();
+
+	void shutdown();
+		
+	void submitRequest(Request* request);
+	static S32 llcdCallback(const char*, S32, S32);
+	void cancel();
+
+	void setMeshData(LLCDMeshData& mesh);
+	void doDecomposition();
+	void doDecompositionSingleHull();
+
+	virtual void run();
+	
+	void completeCurrent();
+	void notifyCompleted();
+
+	std::map<std::string, S32> mStageID;
+
+	typedef std::queue<LLPointer<Request> > request_queue;
+	request_queue mRequestQ;
+
+	LLPointer<Request> mCurRequest;
+
+	std::queue<LLPointer<Request> > mCompletedQ;
+
+};
+
+class LLMeshRepoThread : public LLThread
+{
+public:
+
+	static S32 sActiveHeaderRequests;
+	static S32 sActiveLODRequests;
+	static U32 sMaxConcurrentRequests;
+
+	LLCurlRequest* mCurlRequest;
+	LLMutex*	mMutex;
+	LLMutex*	mHeaderMutex;
+	LLCondition* mSignal;
+
+	bool mWaiting;
+
+	//map of known mesh headers
+	typedef std::map<LLUUID, LLSD> mesh_header_map;
+	mesh_header_map mMeshHeader;
+	
+	std::map<LLUUID, U32> mMeshHeaderSize;
+	std::map<LLUUID, U32> mMeshResourceCost;
+
+	class HeaderRequest
+	{ 
+	public:
+		const LLVolumeParams mMeshParams;
+
+		HeaderRequest(const LLVolumeParams&  mesh_params)
+			: mMeshParams(mesh_params)
+		{
+		}
+
+		bool operator<(const HeaderRequest& rhs) const
+		{
+			return mMeshParams < rhs.mMeshParams;
+		}
+	};
+
+	class LODRequest
+	{
+	public:
+		LLVolumeParams  mMeshParams;
+		S32 mLOD;
+		F32 mScore;
+
+		LODRequest(const LLVolumeParams&  mesh_params, S32 lod)
+			: mMeshParams(mesh_params), mLOD(lod), mScore(0.f)
+		{
+		}
+	};
+
+	struct CompareScoreGreater
+	{
+		bool operator()(const LODRequest& lhs, const LODRequest& rhs)
+		{
+			return lhs.mScore > rhs.mScore; // greatest = first
+		}
+	};
+	
+
+	class LoadedMesh
+	{
+	public:
+		LLPointer<LLVolume> mVolume;
+		LLVolumeParams mMeshParams;
+		S32 mLOD;
+
+		LoadedMesh(LLVolume* volume, const LLVolumeParams&  mesh_params, S32 lod)
+			: mVolume(volume), mMeshParams(mesh_params), mLOD(lod)
+		{
+		}
+
+	};
+
+	//set of requested skin info
+	std::set<LLUUID> mSkinRequests;
+	
+	//queue of completed skin info requests
+	std::queue<LLMeshSkinInfo> mSkinInfoQ;
+
+	//set of requested decompositions
+	std::set<LLUUID> mDecompositionRequests;
+
+	//set of requested physics shapes
+	std::set<LLUUID> mPhysicsShapeRequests;
+
+	//queue of completed Decomposition info requests
+	std::queue<LLModel::Decomposition*> mDecompositionQ;
+
+	//queue of requested headers
+	std::queue<HeaderRequest> mHeaderReqQ;
+
+	//queue of requested LODs
+	std::queue<LODRequest> mLODReqQ;
+
+	//queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD)
+	std::queue<LODRequest> mUnavailableQ;
+
+	//queue of successfully loaded meshes
+	std::queue<LoadedMesh> mLoadedQ;
+
+	//map of pending header requests and currently desired LODs
+	typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;
+	pending_lod_map mPendingLOD;
+
+	static std::string constructUrl(LLUUID mesh_id);
+
+	LLMeshRepoThread();
+	~LLMeshRepoThread();
+
+	virtual void run();
+
+	void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+	bool fetchMeshHeader(const LLVolumeParams& mesh_params);
+	bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+	bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
+	bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
+	bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
+	bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
+	bool physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
+	LLSD& getMeshHeader(const LLUUID& mesh_id);
+
+	void notifyLoadedMeshes();
+	S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+	U32 getResourceCost(const LLUUID& mesh_params);
+
+	void loadMeshSkinInfo(const LLUUID& mesh_id);
+	void loadMeshDecomposition(const LLUUID& mesh_id);
+	void loadMeshPhysicsShape(const LLUUID& mesh_id);
+
+	//send request for skin info, returns true if header info exists 
+	//  (should hold onto mesh_id and try again later if header info does not exist)
+	bool fetchMeshSkinInfo(const LLUUID& mesh_id);
+
+	//send request for decomposition, returns true if header info exists 
+	//  (should hold onto mesh_id and try again later if header info does not exist)
+	bool fetchMeshDecomposition(const LLUUID& mesh_id);
+
+	//send request for PhysicsShape, returns true if header info exists 
+	//  (should hold onto mesh_id and try again later if header info does not exist)
+	bool fetchMeshPhysicsShape(const LLUUID& mesh_id);
+
+
+};
+
+class LLMeshUploadThread : public LLThread 
+{
+public:
+	class DecompRequest : public LLPhysicsDecomp::Request
+	{
+	public:
+		LLPointer<LLModel> mModel;
+		LLPointer<LLModel> mBaseModel;
+
+		LLMeshUploadThread* mThread;
+
+		DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread);
+
+		S32 statusCallback(const char* status, S32 p1, S32 p2) { return 1; }
+		void completed();
+	};
+
+	LLPointer<DecompRequest> mFinalDecomp;
+	bool mPhysicsComplete;
+
+	typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map;
+	hull_map mHullMap;
+
+	typedef std::vector<LLModelInstance> instance_list;
+	instance_list mInstanceList;
+
+	typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
+	instance_map mInstance;
+
+	LLMutex*					mMutex;
+	LLCurlRequest* mCurlRequest;
+	S32				mPendingConfirmations;
+	S32				mPendingUploads;
+	S32				mPendingCost;
+	LLVector3		mOrigin;
+	bool			mFinished;	
+	bool			mUploadTextures;
+	bool			mUploadSkin;
+	bool			mUploadJoints;
+	BOOL            mDiscarded ;
+
+	LLHost			mHost;
+	std::string		mUploadObjectAssetCapability;
+	std::string		mNewInventoryCapability;
+	std::string		mWholeModelUploadCapability;
+
+	std::queue<LLMeshUploadData> mUploadQ;
+	std::queue<LLMeshUploadData> mConfirmedQ;
+	std::queue<LLModelInstance> mInstanceQ;
+
+	std::queue<LLTextureUploadData> mTextureQ;
+	std::queue<LLTextureUploadData> mConfirmedTextureQ;
+
+	std::map<LLViewerFetchedTexture*, LLTextureUploadData> mTextureMap;
+
+	LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures,
+			bool upload_skin, bool upload_joints);
+	~LLMeshUploadThread();
+
+	void uploadTexture(LLTextureUploadData& data);
+	void doUploadTexture(LLTextureUploadData& data);
+	void sendCostRequest(LLTextureUploadData& data);
+	void priceResult(LLTextureUploadData& data, const LLSD& content);
+	void onTextureUploaded(LLTextureUploadData& data);
+
+	void uploadModel(LLMeshUploadData& data);
+	void sendCostRequest(LLMeshUploadData& data);
+	void doUploadModel(LLMeshUploadData& data);
+	void onModelUploaded(LLMeshUploadData& data);
+	void createObjects(LLMeshUploadData& data);
+	LLSD createObject(LLModelInstance& instance);
+	void priceResult(LLMeshUploadData& data, const LLSD& content);
+
+	bool finished() { return mFinished; }
+	virtual void run();
+	void preStart();
+	void discard() ;
+	BOOL isDiscarded();
+
+	void doWholeModelUpload();
+	void doIterativeUpload();
+
+	void wholeModelToLLSD(LLSD& dest, bool include_textures);
+
+	void decomposeMeshMatrix(LLMatrix4& transformation,
+							 LLVector3& result_pos,
+							 LLQuaternion& result_rot,
+							 LLVector3& result_scale);
+};
+
+class LLMeshRepository
+{
+public:
+
+	//metrics
+	static U32 sBytesReceived;
+	static U32 sHTTPRequestCount;
+	static U32 sHTTPRetryCount;
+	static U32 sCacheBytesRead;
+	static U32 sCacheBytesWritten;
+	static U32 sPeakKbps;
+	
+	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1);
+
+	LLMeshRepository();
+
+	void init();
+	void shutdown();
+	S32 update() ;
+
+	//mesh management functions
+	S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
+	
+	void notifyLoadedMeshes();
+	void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
+	void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
+	void notifySkinInfoReceived(LLMeshSkinInfo& info);
+	void notifyDecompositionReceived(LLModel::Decomposition* info);
+
+	S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+	static S32 getActualMeshLOD(LLSD& header, S32 lod);
+	U32 calcResourceCost(LLSD& header);
+	U32 getResourceCost(const LLUUID& mesh_params);
+	const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id);
+	LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id);
+	void fetchPhysicsShape(const LLUUID& mesh_id);
+	bool hasPhysicsShape(const LLUUID& mesh_id);
+	
+	void buildHull(const LLVolumeParams& params, S32 detail);
+	void buildPhysicsMesh(LLModel::Decomposition& decomp);
+
+	LLSD& getMeshHeader(const LLUUID& mesh_id);
+
+	void uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
+			bool upload_skin, bool upload_joints);
+
+	S32 getMeshSize(const LLUUID& mesh_id, S32 lod);
+
+	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
+	mesh_load_map mLoadingMeshes[4];
+	
+	typedef std::map<LLUUID, LLMeshSkinInfo> skin_map;
+	skin_map mSkinMap;
+
+	typedef std::map<LLUUID, LLModel::Decomposition*> decomposition_map;
+	decomposition_map mDecompositionMap;
+
+	LLMutex*					mMeshMutex;
+	
+	std::vector<LLMeshRepoThread::LODRequest> mPendingRequests;
+	
+	//list of mesh ids awaiting skin info
+	std::set<LLUUID> mLoadingSkins;
+
+	//list of mesh ids that need to send skin info fetch requests
+	std::queue<LLUUID> mPendingSkinRequests;
+
+	//list of mesh ids awaiting decompositions
+	std::set<LLUUID> mLoadingDecompositions;
+
+	//list of mesh ids that need to send decomposition fetch requests
+	std::queue<LLUUID> mPendingDecompositionRequests;
+	
+	//list of mesh ids awaiting physics shapes
+	std::set<LLUUID> mLoadingPhysicsShapes;
+
+	//list of mesh ids that need to send physics shape fetch requests
+	std::queue<LLUUID> mPendingPhysicsShapeRequests;
+	
+	U32 mMeshThreadCount;
+
+	void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header);
+	
+	LLMeshRepoThread* mThread;
+	std::vector<LLMeshUploadThread*> mUploads;
+	std::vector<LLMeshUploadThread*> mUploadWaitList;
+
+	LLPhysicsDecomp* mDecompThread;
+	
+	class inventory_data
+	{
+	public:
+		LLSD mPostData;
+		LLSD mResponse;
+
+		inventory_data(const LLSD& data, const LLSD& content)
+			: mPostData(data), mResponse(content)
+		{
+		}
+	};
+
+	std::queue<inventory_data> mInventoryQ;
+
+	std::queue<LLSD> mUploadErrorQ;
+
+	void uploadError(LLSD& args);
+	void updateInventory(inventory_data data);
+
+	std::string mGetMeshCapability;
+
+};
+
+extern LLMeshRepository gMeshRepo;
+
+#endif
+
diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h
index 6805630ef19cb832b0950c6f0e021ae591fcc449..d64fdbe6a5aa2836193f6f119088f55296bf9f6a 100644
--- a/indra/newview/llnamelistctrl.h
+++ b/indra/newview/llnamelistctrl.h
@@ -1,25 +1,25 @@
-/** 
+/**
  * @file llnamelistctrl.h
  * @brief A list of names, automatically refreshing from the name cache.
  *
  * $LicenseInfo:firstyear=2003&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, Linden Research, Inc.
- * 
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation;
  * version 2.1 of the License only.
- * 
+ *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
+ *
  * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  * $/LicenseInfo$
  */
@@ -58,7 +58,7 @@ class LLNameListCtrl
 		NameItem()
 		:	name("name"),
 			target("target", INDIVIDUAL)
-		{}		
+		{}
 	};
 
 	struct NameColumn : public LLInitParam::Choice<NameColumn>
@@ -83,7 +83,7 @@ class LLNameListCtrl
 	LLNameListCtrl(const Params&);
 	friend class LLUICtrlFactory;
 public:
-	// Add a user to the list by name.  It will be added, the name 
+	// Add a user to the list by name.  It will be added, the name
 	// requested from the cache, and updated as necessary.
 	void addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM,
 					 BOOL enabled = TRUE, const std::string& suffix = LLStringUtil::null);
@@ -92,7 +92,7 @@ class LLNameListCtrl
 	/*virtual*/ LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
 	LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM, const std::string& suffix = LLStringUtil::null);
 
-	// Add a user to the list by name.  It will be added, the name 
+	// Add a user to the list by name.  It will be added, the name
 	// requested from the cache, and updated as necessary.
 	void addGroupNameItem(const LLUUID& group_id, EAddPosition pos = ADD_BOTTOM,
 						  BOOL enabled = TRUE);
@@ -126,7 +126,7 @@ class LLNameListCtrl
 
 /**
  * LLNameListCtrl item
- * 
+ *
  * We don't use LLScrollListItem to be able to override getUUID(), which is needed
  * because the name list item value is not simply an UUID but a map (uuid, is_group).
  */
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index bce496cbad6f52f7c5fe34fecf3d8318a9a800a7..07c7f3598965cf24c01619675e81cf0841b536bb 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -376,6 +376,11 @@ struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor
 			return true;
 		}
 
+		if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
+		{
+			return true;
+		}
+
 		bool set_aligned = true;
 		if (facep == mCenterFace)
 		{
@@ -418,6 +423,12 @@ struct LLPanelFaceGetIsAlignedTEFunctor : public LLSelectedTEFunctor
 		{
 			return false;
 		}
+
+		if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
+		{ //volume face does not exist, can't be aligned
+			return false;
+		}
+
 		if (facep == mCenterFace)
 		{
 			return true;
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
index cdf6e51bf811945e2aebb00cff22a913d5b340cb..e64192c2ae907604fd18115ea3588c46935baa24 100644
--- a/indra/newview/llpanelgroupnotices.cpp
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -154,6 +154,7 @@ BOOL LLGroupDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 		case DAD_ANIMATION:
 		case DAD_GESTURE:
 		case DAD_CALLINGCARD:
+		case DAD_MESH:
 		{
 			LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
 			if(gInventory.getItem(inv_item->getUUID())
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 27f341b4f6d449a9711a506c3bdd27adcaa40cb1..d0810d0772e0b54f29a72dd3976709b8b8608d0b 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -34,6 +34,7 @@
 #include "llmd5.h"
 #include "llsecondlifeurls.h"
 #include "v4color.h"
+#include "llversionviewer.h"
 
 #include "llappviewer.h"
 #include "llbutton.h"
@@ -747,12 +748,20 @@ void LLPanelLogin::loadLoginPage()
 								   LLVersionInfo::getShortVersion().c_str(),
 								   LLVersionInfo::getBuild());
 
-	char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0);
+	char* curl_channel ;
 	char* curl_version = curl_escape(version.c_str(), 0);
 
+	if(strcmp(LLVersionInfo::getChannel().c_str(), LL_CHANNEL))
+	{
+		curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0);
+	}
+	else //if LL_CHANNEL, direct it to "Second Life Beta Viewer".
+	{
+		curl_channel = curl_escape("Second Life Beta Viewer", 0);		
+	}
 	oStr << "&channel=" << curl_channel;
 	oStr << "&version=" << curl_version;
-
+	
 	curl_free(curl_channel);
 	curl_free(curl_version);
 
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index f79a1bb5ab109e6429bd67811d1f869f2904c073..bc4998dd0cfd170244cfc4a15a285525360704e0 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -686,6 +686,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_mesh")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_MESH));
 	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));
@@ -737,6 +738,12 @@ void LLFloaterInventoryFinder::draw()
 		filtered_by_all_types = FALSE;
 	}
 
+	if (!getChild<LLUICtrl>("check_mesh")->getValue())
+	{
+		filter &= ~(0x1 << LLInventoryType::IT_MESH);
+		filtered_by_all_types = FALSE;
+	}
+
 	if (!getChild<LLUICtrl>("check_notecard")->getValue())
 	{
 		filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);
@@ -833,6 +840,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_mesh")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_notecard")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_object")->setValue(TRUE);
 	self->getChild<LLUICtrl>("check_script")->setValue(TRUE);
@@ -852,6 +860,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_mesh")->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/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp
index a7f1ab28fd6f67fee3c28fac64968328233c7339..2bbd15ae1175eb31200dada6f5d3405865e33c0d 100644
--- a/indra/newview/llpanelnearbymedia.cpp
+++ b/indra/newview/llpanelnearbymedia.cpp
@@ -356,7 +356,7 @@ void LLPanelNearByMedia::updateListItem(LLScrollListItem* item, LLViewerMediaImp
 		debug_str += llformat("%g/", (float)impl->getInterest());
 		
 		// proximity distance is actually distance squared -- display it as straight distance.
-		debug_str += llformat("%g/", fsqrtf(impl->getProximityDistance()));
+		debug_str += llformat("%g/", (F32) sqrt(impl->getProximityDistance()));
 		
 		//			s += llformat("%g/", (float)impl->getCPUUsage());
 		//			s += llformat("%g/", (float)impl->getApproximateTextureInterest());
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index a0c320ba19d749ded2acf86acd39185188f4dbc7..64af6c2157b97cf1d6cdea643288cd6ec9de8a27 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -131,7 +131,8 @@ BOOL	LLPanelObject::postBuild()
 	// Phantom checkbox
 	mCheckPhantom = getChild<LLCheckBoxCtrl>("Phantom Checkbox Ctrl");
 	childSetCommitCallback("Phantom Checkbox Ctrl",onCommitPhantom,this);
-	
+       
+
 	// Position
 	mLabelPosition = getChild<LLTextBox>("label position");
 	mCtrlPosX = getChild<LLSpinCtrl>("Pos X");
@@ -519,6 +520,7 @@ void LLPanelObject::getState( )
 	mCheckPhantom->set( mIsPhantom );
 	mCheckPhantom->setEnabled( roots_selected>0 && editable && !is_flexible );
 
+       
 #if 0 // 1.9.2
 	mCastShadows = root_objectp->flagCastShadows();
 	mCheckCastShadows->set( mCastShadows );
@@ -569,6 +571,7 @@ void LLPanelObject::getState( )
 	BOOL enabled = FALSE;
 	BOOL hole_enabled = FALSE;
 	F32 scale_x=1.f, scale_y=1.f;
+	BOOL isMesh = FALSE;
 	
 	if( !objectp || !objectp->getVolume() || !editable || !single_volume)
 	{
@@ -599,10 +602,9 @@ void LLPanelObject::getState( )
 		// Only allowed to change these parameters for objects
 		// that you have permissions on AND are not attachments.
 		enabled = root_objectp->permModify();
-
-		const LLVolumeParams &volume_params = objectp->getVolume()->getParams();
-
+		
 		// Volume type
+		const LLVolumeParams &volume_params = objectp->getVolume()->getParams();
 		U8 path = volume_params.getPathParams().getCurveType();
 		U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
 		U8 profile	= profile_and_hole & LL_PCODE_PROFILE_MASK;
@@ -833,7 +835,7 @@ void LLPanelObject::getState( )
 		}
 		mSpinSkew->set( skew );
 	}
-
+	
 	// Compute control visibility, label names, and twist range.
 	// Start with defaults.
 	BOOL cut_visible                = TRUE;
@@ -1101,7 +1103,9 @@ void LLPanelObject::getState( )
 
 	if (selected_item == MI_SCULPT)
 	{
-        LLUUID id;
+
+
+		LLUUID id;
 		LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
 
 		
@@ -1113,26 +1117,29 @@ void LLPanelObject::getState( )
 				mSculptTypeRevert    = sculpt_params->getSculptType();
 			}
 		
+			U8 sculpt_type = sculpt_params->getSculptType();
+			U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+			BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
+			BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+			isMesh = (sculpt_stitching == LL_SCULPT_TYPE_MESH);
+
 			LLTextureCtrl*  mTextureCtrl = getChild<LLTextureCtrl>("sculpt texture control");
 			if(mTextureCtrl)
 			{
 				mTextureCtrl->setTentative(FALSE);
-				mTextureCtrl->setEnabled(editable);
+				mTextureCtrl->setEnabled(editable && !isMesh);
 				if (editable)
 					mTextureCtrl->setImageAssetID(sculpt_params->getSculptTexture());
 				else
 					mTextureCtrl->setImageAssetID(LLUUID::null);
 			}
 
-			U8 sculpt_type = sculpt_params->getSculptType();
-			U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
-			BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
-			BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+			mComboBaseType->setEnabled(!isMesh);
 			
 			if (mCtrlSculptType)
 			{
 				mCtrlSculptType->setCurrentByIndex(sculpt_stitching);
-				mCtrlSculptType->setEnabled(editable);
+				mCtrlSculptType->setEnabled(editable && !isMesh);
 			}
 
 			if (mCtrlSculptMirror)
@@ -1151,14 +1158,14 @@ void LLPanelObject::getState( )
 			{
 				mLabelSculptType->setEnabled(TRUE);
 			}
+			
 		}
 	}
 	else
 	{
-		mSculptTextureRevert = LLUUID::null;
+		mSculptTextureRevert = LLUUID::null;		
 	}
 
-	
 	//----------------------------------------------------------------------------
 
 	mObject = objectp;
@@ -1786,6 +1793,17 @@ void LLPanelObject::sendSculpt()
 	if (mCtrlSculptType)
 		sculpt_type |= mCtrlSculptType->getCurrentIndex();
 
+	bool enabled = sculpt_type != LL_SCULPT_TYPE_MESH;
+
+	if (mCtrlSculptMirror)
+	{
+		mCtrlSculptMirror->setEnabled(enabled ? TRUE : FALSE);
+	}
+	if (mCtrlSculptInvert)
+	{
+		mCtrlSculptInvert->setEnabled(enabled ? TRUE : FALSE);
+	}
+	
 	if ((mCtrlSculptMirror) && (mCtrlSculptMirror->get()))
 		sculpt_type |= LL_SCULPT_FLAG_MIRROR;
 
@@ -1808,6 +1826,26 @@ void LLPanelObject::refresh()
 	{
 		mRootObject = NULL;
 	}
+	
+	bool enable_mesh = gSavedSettings.getBOOL("MeshEnabled") && 
+					   gAgent.getRegion() &&
+					   !gAgent.getRegion()->getCapability("GetMesh").empty();
+
+	F32 max_scale = get_default_max_prim_scale(LLPickInfo::isFlora(mObject));
+
+	getChild<LLSpinCtrl>("Scale X")->setMaxValue(max_scale);
+	getChild<LLSpinCtrl>("Scale Y")->setMaxValue(max_scale);
+	getChild<LLSpinCtrl>("Scale Z")->setMaxValue(max_scale);
+
+	BOOL found = mCtrlSculptType->itemExists("Mesh");
+	if (enable_mesh && !found)
+	{
+		mCtrlSculptType->add("Mesh");
+	}
+	else if (!enable_mesh && found)
+	{
+		mCtrlSculptType->remove("Mesh");
+	}
 }
 
 
@@ -1894,6 +1932,7 @@ void LLPanelObject::clearCtrls()
 	mCheckTemporary	->setEnabled( FALSE );
 	mCheckPhantom	->set(FALSE);
 	mCheckPhantom	->setEnabled( FALSE );
+	
 #if 0 // 1.9.2
 	mCheckCastShadows->set(FALSE);
 	mCheckCastShadows->setEnabled( FALSE );
diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h
index e07bf007ec1d2e952a224eb7b48194f2a17c27a9..e2f2a4400d7fecbabc6c4868894bf08c7b261404 100644
--- a/indra/newview/llpanelobject.h
+++ b/indra/newview/llpanelobject.h
@@ -62,14 +62,14 @@ class LLPanelObject : public LLPanel
 	static void 	onCommitPosition(		LLUICtrl* ctrl, void* userdata);
 	static void 	onCommitScale(			LLUICtrl* ctrl, void* userdata);
 	static void 	onCommitRotation(		LLUICtrl* ctrl, void* userdata);
-	static void 	onCommitPhysics(		LLUICtrl* ctrl, void* userdata);
 	static void 	onCommitTemporary(		LLUICtrl* ctrl, void* userdata);
 	static void 	onCommitPhantom(		LLUICtrl* ctrl, void* userdata);
 	static void 	onCommitCastShadows(	LLUICtrl* ctrl, void* userdata);
+	static void 	onCommitPhysics(		LLUICtrl* ctrl, void* userdata);
+	static void 	onCommitMaterial(		LLUICtrl* ctrl, void* userdata);
 
 	static void 	onCommitParametric(LLUICtrl* ctrl, void* userdata);
 
-	static void 	onCommitMaterial(		LLUICtrl* ctrl, void* userdata);
 
 	void     		onCommitSculpt(const LLSD& data);
 	void     		onCancelSculpt(const LLSD& data);
@@ -87,6 +87,7 @@ class LLPanelObject : public LLPanel
 	void			sendIsPhysical();
 	void			sendIsTemporary();
 	void			sendIsPhantom();
+
 	void			sendCastShadows();
 	void            sendSculpt();
 	
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index 0b6267c9e6e70ba098158ec471ff185f1ad5b27f..bfe6cab52f378969b6b51b8874eb2e44d11b68e9 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -802,6 +802,7 @@ BOOL LLTaskCategoryBridge::dragOrDrop(MASK mask, BOOL drop,
 		case DAD_ANIMATION:
 		case DAD_GESTURE:
 		case DAD_CALLINGCARD:
+		case DAD_MESH:
 			accept = LLToolDragAndDrop::isInventoryDropAcceptable(object, (LLViewerInventoryItem*)cargo_data);
 			if(accept && drop)
 			{
@@ -1227,6 +1228,116 @@ LLUIImagePtr LLTaskWearableBridge::getIcon() const
 	return LLInventoryIcon::getIcon(mAssetType, mInventoryType, mFlags, FALSE );
 }
 
+///----------------------------------------------------------------------------
+/// Class LLTaskMeshBridge
+///----------------------------------------------------------------------------
+
+class LLTaskMeshBridge : public LLTaskInvFVBridge
+{
+public:
+	LLTaskMeshBridge(
+		LLPanelObjectInventory* panel,
+		const LLUUID& uuid,
+		const std::string& name);
+
+	virtual LLUIImagePtr getIcon() const;
+	virtual void openItem();
+	virtual void performAction(LLInventoryModel* model, std::string action);
+	virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+};
+
+LLTaskMeshBridge::LLTaskMeshBridge(
+	LLPanelObjectInventory* panel,
+	const LLUUID& uuid,
+	const std::string& name) :
+	LLTaskInvFVBridge(panel, uuid, name)
+{
+}
+
+LLUIImagePtr LLTaskMeshBridge::getIcon() const
+{
+	return LLInventoryIcon::getIcon(LLAssetType::AT_MESH, LLInventoryType::IT_MESH, 0, FALSE);
+}
+
+void LLTaskMeshBridge::openItem()
+{
+	// open mesh
+}
+
+
+// virtual
+void LLTaskMeshBridge::performAction(LLInventoryModel* model, std::string action)
+{
+	if (action == "mesh action")
+	{
+		LLInventoryItem* item = findItem();
+		if(item)
+		{
+			// do action
+		}
+	}
+	LLTaskInvFVBridge::performAction(model, action);
+}
+
+void LLTaskMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+	LLInventoryItem* item = findItem();
+	if(!item) return;
+	std::vector<std::string> items;
+	std::vector<std::string> disabled_items;
+
+	if(item->getPermissions().getOwner() != gAgent.getID()
+	   && item->getSaleInfo().isForSale())
+	{
+		items.push_back(std::string("Task Buy"));
+
+		std::string label= LLTrans::getString("Buy");
+		// Check the price of the item.
+		S32 price = getPrice();
+		if (-1 == price)
+		{
+			llwarns << "label_buy_task_bridged_item: Invalid price" << llendl;
+		}
+		else
+		{
+			std::ostringstream info;
+			info <<  LLTrans::getString("BuyforL$") << price;
+			label.assign(info.str());
+		}
+
+		const LLView::child_list_t *list = menu.getChildList();
+		LLView::child_list_t::const_iterator itor;
+		for (itor = list->begin(); itor != list->end(); ++itor)
+		{
+			std::string name = (*itor)->getName();
+			LLMenuItemCallGL* menu_itemp = dynamic_cast<LLMenuItemCallGL*>(*itor);
+			if (name == "Task Buy" && menu_itemp)
+			{
+				menu_itemp->setLabel(label);
+			}
+		}
+	}
+	else
+	{
+		items.push_back(std::string("Task Open")); 
+		if (!isItemCopyable())
+		{
+			disabled_items.push_back(std::string("Task Open"));
+		}
+	}
+	items.push_back(std::string("Task Properties"));
+	if(isItemRenameable())
+	{
+		items.push_back(std::string("Task Rename"));
+	}
+	if(isItemRemovable())
+	{
+		items.push_back(std::string("Task Remove"));
+	}
+
+
+	hide_context_entries(menu, items, disabled_items);
+}
 
 ///----------------------------------------------------------------------------
 /// LLTaskInvFVBridge impl
@@ -1307,6 +1418,11 @@ LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelObjectInventory*
 						 object->getUUID(),
 						 object->getName());
 		break;
+	case LLAssetType::AT_MESH:
+		new_bridge = new LLTaskMeshBridge(panel,
+										  object->getUUID(),
+										  object->getName());
+		break;
 	default:
 		llinfos << "Unhandled inventory type (llassetstorage.h): "
 				<< (S32)type << llendl;
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 82ff6c3487e680e55eb9194e0150f3274d2272e6..933b40ec7914955cdb2a4bc42eb242ee557f338c 100644
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -61,6 +61,7 @@
 #include "llwindow.h"
 #include "llwindowshade.h"
 #include "llfloatertools.h"  // to enable hide if build tools are up
+#include "llvector4a.h"
 
 // Functions pulled from pipeline.cpp
 glh::matrix4f glh_get_current_modelview();
@@ -577,7 +578,9 @@ void LLPanelPrimMediaControls::updateShape()
 		{
 			const LLVolumeFace& vf = volume->getVolumeFace(mTargetObjectFace);
 			
-			const LLVector3* ext = vf.mExtents;
+			LLVector3 ext[2];
+			ext[0].set(vf.mExtents[0].getF32ptr());
+			ext[1].set(vf.mExtents[1].getF32ptr());
 			
 			LLVector3 center = (ext[0]+ext[1])*0.5f;
 			LLVector3 size = (ext[1]-ext[0])*0.5f;
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index 065c4d0b92f3987f9fa555efd994eb8bc3d70d5d..c443814c89bdbcf3ec91ed3502f5e6893ad8e1a3 100644
--- a/indra/newview/llpanelvolume.cpp
+++ b/indra/newview/llpanelvolume.cpp
@@ -71,6 +71,11 @@
 #include "lldrawpool.h"
 #include "lluictrlfactory.h"
 
+// For mesh physics
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llmeshrepository.h"
+
 // "Features" Tab
 
 BOOL	LLPanelVolume::postBuild()
@@ -129,6 +134,29 @@ BOOL	LLPanelVolume::postBuild()
 		getChild<LLUICtrl>("Light Ambiance")->setValidateBeforeCommit( precommitValidate);
 	}
 	
+	// PHYSICS Parameters
+	{
+		// PhysicsShapeType combobox
+		mComboPhysicsShapeType = getChild<LLComboBox>("Physics Shape Type Combo Ctrl");
+		mComboPhysicsShapeType->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsShapeType, this, _1, mComboPhysicsShapeType));
+	
+		// PhysicsGravity
+		mSpinPhysicsGravity = getChild<LLSpinCtrl>("Physics Gravity");
+		mSpinPhysicsGravity->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsGravity, this, _1, mSpinPhysicsGravity));
+
+		// PhysicsFriction
+		mSpinPhysicsFriction = getChild<LLSpinCtrl>("Physics Friction");
+		mSpinPhysicsFriction->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsFriction, this, _1, mSpinPhysicsFriction));
+
+		// PhysicsDensity
+		mSpinPhysicsDensity = getChild<LLSpinCtrl>("Physics Density");
+		mSpinPhysicsDensity->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsDensity, this, _1, mSpinPhysicsDensity));
+
+		// PhysicsRestitution
+		mSpinPhysicsRestitution = getChild<LLSpinCtrl>("Physics Restitution");
+		mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution));
+	}
+	
 	// Start with everyone disabled
 	clearCtrls();
 
@@ -351,6 +379,54 @@ void LLPanelVolume::getState( )
 		getChildView("FlexForceZ")->setEnabled(false);
 	}
 	
+	// Physics properties
+	
+	mSpinPhysicsGravity->set(objectp->getPhysicsGravity());
+	mSpinPhysicsGravity->setEnabled(editable);
+
+	mSpinPhysicsFriction->set(objectp->getPhysicsFriction());
+	mSpinPhysicsFriction->setEnabled(editable);
+	
+	mSpinPhysicsDensity->set(objectp->getPhysicsDensity());
+	mSpinPhysicsDensity->setEnabled(editable);
+	
+	mSpinPhysicsRestitution->set(objectp->getPhysicsRestitution());
+	mSpinPhysicsRestitution->setEnabled(editable);
+
+	// update the physics shape combo to include allowed physics shapes
+	mComboPhysicsShapeType->removeall();
+	mComboPhysicsShapeType->add(getString("None"), LLSD(1));
+
+	BOOL isMesh = FALSE;
+	LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+	if (sculpt_params)
+	{
+		U8 sculpt_type = sculpt_params->getSculptType();
+		U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+		isMesh = (sculpt_stitching == LL_SCULPT_TYPE_MESH);
+	}
+
+	if(isMesh && objectp)
+	{
+		const LLVolumeParams &volume_params = objectp->getVolume()->getParams();
+		LLUUID mesh_id = volume_params.getSculptID();
+		if(gMeshRepo.hasPhysicsShape(mesh_id))
+		{
+			// if a mesh contains an uploaded or decomposed physics mesh,
+			// allow 'Prim'
+			mComboPhysicsShapeType->add(getString("Prim"), LLSD(0));			
+		}
+	}
+	else
+	{
+		// simple prims always allow physics shape prim
+		mComboPhysicsShapeType->add(getString("Prim"), LLSD(0));	
+	}
+
+	mComboPhysicsShapeType->add(getString("Convex Hull"), LLSD(2));	
+	mComboPhysicsShapeType->setValue(LLSD(objectp->getPhysicsShapeType()));
+	mComboPhysicsShapeType->setEnabled(editable);
+
 	mObject = objectp;
 	mRootObject = root_objectp;
 }
@@ -384,6 +460,17 @@ void LLPanelVolume::refresh()
 	getChildView("Light Ambiance")->setVisible( visible);
 	getChildView("light texture control")->setVisible( visible);
 
+	bool enable_mesh = gSavedSettings.getBOOL("MeshEnabled") && 
+					   gAgent.getRegion() &&
+					   !gAgent.getRegion()->getCapability("GetMesh").empty();
+
+	getChildView("label physicsshapetype")->setVisible(enable_mesh);
+	getChildView("Physics Shape Type Combo Ctrl")->setVisible(enable_mesh);
+	getChildView("Physics Gravity")->setVisible(enable_mesh);
+	getChildView("Physics Material Override")->setVisible(enable_mesh);
+	getChildView("Physics Friction")->setVisible(enable_mesh);
+	getChildView("Physics Density")->setVisible(enable_mesh);
+	getChildView("Physics Restitution")->setVisible(enable_mesh);
 }
 
 
@@ -430,6 +517,11 @@ void LLPanelVolume::clearCtrls()
 	getChildView("FlexForceX")->setEnabled(false);
 	getChildView("FlexForceY")->setEnabled(false);
 	getChildView("FlexForceZ")->setEnabled(false);
+
+	mSpinPhysicsGravity->setEnabled(FALSE);
+	mSpinPhysicsFriction->setEnabled(FALSE);
+	mSpinPhysicsDensity->setEnabled(FALSE);
+	mSpinPhysicsRestitution->setEnabled(FALSE);
 }
 
 //
@@ -482,6 +574,48 @@ void LLPanelVolume::sendIsFlexible()
 	llinfos << "update flexible sent" << llendl;
 }
 
+void LLPanelVolume::sendPhysicsShapeType(LLUICtrl* ctrl, void* userdata)
+{
+	U8 type = ctrl->getValue().asInteger();
+	LLSelectMgr::getInstance()->selectionSetPhysicsType(type);
+
+	refreshCost();
+}
+
+void LLPanelVolume::sendPhysicsGravity(LLUICtrl* ctrl, void* userdata)
+{
+	F32 val = ctrl->getValue().asReal();
+	LLSelectMgr::getInstance()->selectionSetGravity(val);
+}
+
+void LLPanelVolume::sendPhysicsFriction(LLUICtrl* ctrl, void* userdata)
+{
+	F32 val = ctrl->getValue().asReal();
+	LLSelectMgr::getInstance()->selectionSetFriction(val);
+}
+
+void LLPanelVolume::sendPhysicsRestitution(LLUICtrl* ctrl, void* userdata)
+{
+	F32 val = ctrl->getValue().asReal();
+	LLSelectMgr::getInstance()->selectionSetRestitution(val);
+}
+
+void LLPanelVolume::sendPhysicsDensity(LLUICtrl* ctrl, void* userdata)
+{
+	F32 val = ctrl->getValue().asReal();
+	LLSelectMgr::getInstance()->selectionSetDensity(val);
+}
+
+void LLPanelVolume::refreshCost()
+{
+	LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+	
+	if (obj)
+	{
+		obj->getObjectCost();
+	}
+}
+
 void LLPanelVolume::onLightCancelColor(const LLSD& data)
 {
 	LLColorSwatchCtrl*	LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
index 90a2abda25e3ec1798662ae0431be90f1449fa67..776a2c1f4abcfc8f36f941f775bd04997acaa785 100644
--- a/indra/newview/llpanelvolume.h
+++ b/indra/newview/llpanelvolume.h
@@ -64,6 +64,8 @@ class LLPanelVolume : public LLPanel
 	static void 	onCommitIsFlexible(		LLUICtrl* ctrl, void* userdata);
 	static void 	onCommitFlexible(		LLUICtrl* ctrl, void* userdata);
 
+	static void     onCommitPhysicsParam(       LLUICtrl* ctrl, void* userdata);
+
 	void		onLightCancelColor(const LLSD& data);
 	void		onLightSelectColor(const LLSD& data);
 
@@ -73,8 +75,15 @@ class LLPanelVolume : public LLPanel
 
 protected:
 	void			getState();
+	void			refreshCost();
 
 protected:
+	void            sendPhysicsShapeType(LLUICtrl* ctrl, void* userdata);
+	void            sendPhysicsGravity(LLUICtrl* ctrl, void* userdata);
+	void            sendPhysicsFriction(LLUICtrl* ctrl, void* userdata);
+	void            sendPhysicsRestitution(LLUICtrl* ctrl, void* userdata);
+	void            sendPhysicsDensity(LLUICtrl* ctrl, void* userdata);
+
 /*
 	LLTextBox*		mLabelSelectSingleMessage;
 	// Light
@@ -99,6 +108,12 @@ class LLPanelVolume : public LLPanel
 	LLUUID			mLightSavedTexture;
 	LLPointer<LLViewerObject> mObject;
 	LLPointer<LLViewerObject> mRootObject;
+
+	LLComboBox*     mComboPhysicsShapeType;
+	LLSpinCtrl*     mSpinPhysicsGravity;
+	LLSpinCtrl*     mSpinPhysicsFriction;
+	LLSpinCtrl*     mSpinPhysicsDensity;
+	LLSpinCtrl*     mSpinPhysicsRestitution;
 };
 
 #endif
diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp
index 30a04109bc819357c83bfd49579187f269154d92..e124916c48095131afcec1dc09e9bb0e78a02139 100644
--- a/indra/newview/llphysicsmotion.cpp
+++ b/indra/newview/llphysicsmotion.cpp
@@ -652,7 +652,7 @@ BOOL LLPhysicsMotion::onUpdate(F32 time)
 		const F32 area_for_max_settings = 0.0;
 		const F32 area_for_min_settings = 1400.0;
 		const F32 area_for_this_setting = area_for_max_settings + (area_for_min_settings-area_for_max_settings)*(1.0-lod_factor);
-		const F32 pixel_area = fsqrtf(mCharacter->getPixelArea());
+	        const F32 pixel_area = sqrtf(mCharacter->getPixelArea());
         
 		const BOOL is_self = (dynamic_cast<LLVOAvatarSelf *>(mCharacter) != NULL);
 		if ((pixel_area > area_for_this_setting) || is_self)
diff --git a/indra/newview/llphysicsshapebuilderutil.cpp b/indra/newview/llphysicsshapebuilderutil.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5bfe5c9941c470443abd6bd3e96170040c240a4a
--- /dev/null
+++ b/indra/newview/llphysicsshapebuilderutil.cpp
@@ -0,0 +1,210 @@
+/** 
+ * @file llphysicsshapebuilder.cpp
+ * @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llphysicsshapebuilderutil.h"
+
+/* static */
+void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut )
+{
+	const LLProfileParams& profile_params = volume_params.getProfileParams();
+	const LLPathParams& path_params = volume_params.getPathParams();
+
+	specOut.mScale = scale;
+
+	const F32 avgScale = ( scale[VX] + scale[VY] + scale[VZ] )/3.0f;	
+
+	// count the scale elements that are small
+	S32 min_size_counts = 0;
+	for (S32 i = 0; i < 3; ++i)
+	{
+		if (scale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE)
+		{
+			++min_size_counts;
+		}
+	}
+
+	const bool profile_complete = ( profile_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) &&
+		( profile_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) );
+
+	const bool path_complete =	( path_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) && 
+		( path_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) );
+
+	const bool simple_params = ( volume_params.getHollow() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW/avgScale )
+		&& ( fabs(path_params.getShearX()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale )
+		&& ( fabs(path_params.getShearY()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale )
+		&& ( !volume_params.isMeshSculpt() && !volume_params.isSculpt() );
+
+	if (simple_params && profile_complete)
+	{
+		// Try to create an implicit shape or convexified
+		bool no_taper = ( fabs(path_params.getScaleX() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale )
+			&& ( fabs(path_params.getScaleY() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale );
+
+		bool no_twist = ( fabs(path_params.getTwistBegin()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale )
+			&& ( fabs(path_params.getTwistEnd()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale);
+
+		// Box 
+		if(
+			( profile_params.getCurveType() == LL_PCODE_PROFILE_SQUARE )
+			&& ( path_params.getCurveType() == LL_PCODE_PATH_LINE )
+			&& no_taper
+			&& no_twist
+			)
+		{
+			specOut.mType = PhysicsShapeSpecification::BOX;
+			if ( path_complete )
+			{
+				return;
+			}
+			else
+			{
+				// Side lengths
+				specOut.mScale[VX] = llmax( scale[VX], SHAPE_BUILDER_MIN_GEOMETRY_SIZE );
+				specOut.mScale[VY] = llmax( scale[VY], SHAPE_BUILDER_MIN_GEOMETRY_SIZE );
+				specOut.mScale[VZ] = llmax( scale[VZ] * (path_params.getEnd() - path_params.getBegin()), SHAPE_BUILDER_MIN_GEOMETRY_SIZE );
+
+				specOut.mCenter.set( 0.f, 0.f, 0.5f * scale[VZ] * ( path_params.getEnd() + path_params.getBegin() - 1.0f ) );
+				return;
+			}
+		}
+
+		// Sphere 
+		if(		path_complete
+			&& ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE_HALF )
+			&& ( path_params.getCurveType() == LL_PCODE_PATH_CIRCLE )
+			&& ( fabs(volume_params.getTaper()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale )
+			&& no_twist
+			)
+		{
+			if (   ( scale[VX] == scale[VZ] )
+				&& ( scale[VY] == scale[VZ] ) )
+			{
+				// perfect sphere
+				specOut.mType	= PhysicsShapeSpecification::SPHERE;
+				specOut.mScale  = scale;
+				return;
+			}
+			else if (min_size_counts > 1)
+			{
+				// small or narrow sphere -- we can boxify
+				for (S32 i=0; i<3; ++i)
+				{
+					if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE)
+					{
+						// reduce each small dimension size to split the approximation errors
+						specOut.mScale[i] *= 0.75f;
+					}
+				}
+				specOut.mType  = PhysicsShapeSpecification::BOX;
+				return;
+			}
+		}
+
+		// Cylinder 
+		if(	   (scale[VX] == scale[VY])
+			&& ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE )
+			&& ( path_params.getCurveType() == LL_PCODE_PATH_LINE )
+			&& ( volume_params.getBeginS() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale )
+			&& ( volume_params.getEndS() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) )
+			&& no_taper
+			)
+		{
+			if (min_size_counts > 1)
+			{
+				// small or narrow sphere -- we can boxify
+				for (S32 i=0; i<3; ++i)
+				{
+					if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE)
+					{
+						// reduce each small dimension size to split the approximation errors
+						specOut.mScale[i] *= 0.75f;
+					}
+				}
+
+				specOut.mType = PhysicsShapeSpecification::BOX;
+			}
+			else
+			{
+				specOut.mType = PhysicsShapeSpecification::CYLINDER;
+				F32 length = (volume_params.getPathParams().getEnd() - volume_params.getPathParams().getBegin()) * scale[VZ];
+
+				specOut.mScale[VY] = specOut.mScale[VX];
+				specOut.mScale[VZ] = length;
+				// The minus one below fixes the fact that begin and end range from 0 to 1 not -1 to 1.
+				specOut.mCenter.set( 0.f, 0.f, 0.5f * (volume_params.getPathParams().getBegin() + volume_params.getPathParams().getEnd() - 1.f) * scale[VZ] );
+			}
+
+			return;
+		}
+	}
+
+	if (	(min_size_counts == 3 )
+		// Possible dead code here--who wants to take it out?
+		||	(path_complete
+				&& profile_complete
+				&& ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) 
+				&& (min_size_counts > 1 ) ) 
+		)
+	{
+		// it isn't simple but
+		// we might be able to convexify this shape if the path and profile are complete
+		// or the path is linear and both path and profile are complete --> we can boxify it
+		specOut.mType = PhysicsShapeSpecification::BOX;
+		specOut.mScale = scale;
+		return;
+	}
+
+	// Special case for big, very thin objects - bump the small dimensions up to the COLLISION_TOLERANCE
+	if (min_size_counts == 1		// One dimension is small
+		&& avgScale > 3.f)			//  ... but others are fairly large
+	{
+		for (S32 i = 0; i < 3; ++i)
+		{
+			specOut.mScale[i] = llmax( specOut.mScale[i], COLLISION_TOLERANCE );
+		}
+	}
+
+	if ( volume_params.shouldForceConvex() )
+	{
+		specOut.mType = PhysicsShapeSpecification::USER_CONVEX;
+	}	
+	// Make a simpler convex shape if we can.
+	else if (volume_params.isConvex()			// is convex
+			|| min_size_counts > 1 )			// two or more small dimensions
+	{
+		specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX;
+	}
+	else if ( volume_params.isSculpt() ) // Is a sculpt of any kind (mesh or legacy)
+	{
+		specOut.mType = volume_params.isMeshSculpt() ? PhysicsShapeSpecification::USER_MESH : PhysicsShapeSpecification::SCULPT;
+	}
+	else // Resort to mesh 
+	{
+		specOut.mType = PhysicsShapeSpecification::PRIM_MESH;
+	}
+}
diff --git a/indra/newview/llphysicsshapebuilderutil.h b/indra/newview/llphysicsshapebuilderutil.h
new file mode 100644
index 0000000000000000000000000000000000000000..7dedfb05e223b774741b658619e3960164eb036d
--- /dev/null
+++ b/indra/newview/llphysicsshapebuilderutil.h
@@ -0,0 +1,138 @@
+/** 
+ * @file llphysicsshapebuilder.h
+ * @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_PHYSICS_SHAPE_BUILDER_H
+#define LL_PHYSICS_SHAPE_BUILDER_H
+
+#include "indra_constants.h"
+#include "llvolume.h"
+
+#define USE_SHAPE_QUANTIZATION 0
+
+#define SHAPE_BUILDER_DEFAULT_VOLUME_DETAIL 1
+
+#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW 0.10f
+#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW_SPHERES 0.90f
+#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT 0.05f
+#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER 0.05f
+#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST 0.09f
+#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR 0.05f
+
+const F32 SHAPE_BUILDER_ENTRY_SNAP_SCALE_BIN_SIZE = 0.15f;
+const F32 SHAPE_BUILDER_ENTRY_SNAP_PARAMETER_BIN_SIZE = 0.010f;
+const F32 SHAPE_BUILDER_CONVEXIFICATION_SIZE = 2.f * COLLISION_TOLERANCE;
+const F32 SHAPE_BUILDER_MIN_GEOMETRY_SIZE = 0.5f * COLLISION_TOLERANCE;
+
+class LLPhysicsVolumeParams : public LLVolumeParams
+{
+public:
+
+	LLPhysicsVolumeParams( const LLVolumeParams& params, bool forceConvex ) : 
+		LLVolumeParams( params ),
+		mForceConvex(forceConvex) {}
+
+	bool operator==(const LLPhysicsVolumeParams &params) const
+	{
+		return ( LLVolumeParams::operator==(params) && (mForceConvex == params.mForceConvex) );
+	}
+
+	bool operator!=(const LLPhysicsVolumeParams &params) const
+	{
+		return !operator==(params);
+	}
+
+	bool operator<(const LLPhysicsVolumeParams &params) const
+	{
+		if ( LLVolumeParams::operator!=(params) )
+		{
+			return LLVolumeParams::operator<(params);
+		}
+		return (params.mForceConvex == false) && (mForceConvex == true);	
+	}
+
+	bool shouldForceConvex() const { return mForceConvex; }
+
+private:
+	bool mForceConvex;
+};
+
+
+class LLPhysicsShapeBuilderUtil
+{
+public:
+
+	class PhysicsShapeSpecification
+	{
+	public:
+		enum ShapeType
+		{
+			// Primitive types
+			BOX,
+			SPHERE,
+			CYLINDER,
+
+			USER_CONVEX,	// User specified they wanted the convex hull of the volume
+
+			PRIM_CONVEX,	// Either a volume that is inherently convex but not a primitive type, or a shape
+							// with dimensions such that will convexify it anyway.
+
+ 			SCULPT,			// Special case for traditional sculpts--they are the convex hull of a single particular set of volume params
+
+			USER_MESH,		// A user mesh. May or may not contain a convex decomposition.
+
+			PRIM_MESH,		// A non-convex volume which we have to represent accurately
+
+			INVALID
+		};
+
+		PhysicsShapeSpecification() : 
+		mType( INVALID ),
+		mScale( 0.f, 0.f, 0.f ),
+		mCenter( 0.f, 0.f, 0.f ) {}
+		
+		bool isConvex() { return (mType != USER_MESH && mType != PRIM_MESH && mType != INVALID); }
+		bool isMesh() { return (mType == USER_MESH) || (mType == PRIM_MESH); }
+
+		ShapeType getType() { return mType; }
+		const LLVector3& getScale() { return mScale; }
+		const LLVector3& getCenter() { return mCenter; }
+
+	private:
+		friend class LLPhysicsShapeBuilderUtil;
+
+		ShapeType	mType;
+
+		// Dimensions of an AABB around the shape
+		LLVector3	mScale;
+
+		// Offset of shape from origin of primitive's reference frame
+		LLVector3	mCenter;
+	};
+
+	static void determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut );
+};
+
+#endif //LL_PHYSICS_SHAPE_BUILDER_H
diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp
index bacaa0cd761e9bea5f48294807a637c33d12b4a6..450f9b2be7238a61f594dee75d7a1a3416f3a46e 100644
--- a/indra/newview/llpolymesh.cpp
+++ b/indra/newview/llpolymesh.cpp
@@ -29,7 +29,8 @@
 //-----------------------------------------------------------------------------
 #include "llviewerprecompiledheaders.h"
 
-#include "llpolymesh.h"
+#include "llfasttimer.h"
+#include "llmemory.h"
 
 #include "llviewercontrol.h"
 #include "llxmltree.h"
@@ -39,7 +40,7 @@
 #include "llvolume.h"
 #include "llendianswizzle.h"
 
-#include "llfasttimer.h"
+#include "llpolymesh.h"
 
 #define HEADER_ASCII "Linden Mesh 1.0"
 #define HEADER_BINARY "Linden Binary Mesh 1.0"
@@ -737,58 +738,52 @@ const LLVector2 &LLPolyMeshSharedData::getUVs(U32 index)
 //-----------------------------------------------------------------------------
 LLPolyMesh::LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh)
 {       
-        LLMemType mt(LLMemType::MTYPE_AVATAR_MESH);
-
-        llassert(shared_data);
-
-        mSharedData = shared_data;
-        mReferenceMesh = reference_mesh;
-        mAvatarp = NULL;
-        mVertexData = NULL;
-
-        mCurVertexCount = 0;
-        mFaceIndexCount = 0;
-        mFaceIndexOffset = 0;
-        mFaceVertexCount = 0;
-        mFaceVertexOffset = 0;
-
-        if (shared_data->isLOD() && reference_mesh)
-        {
-                mCoords = reference_mesh->mCoords;
-                mNormals = reference_mesh->mNormals;
-                mScaledNormals = reference_mesh->mScaledNormals;
-                mBinormals = reference_mesh->mBinormals;
-                mScaledBinormals = reference_mesh->mScaledBinormals;
-                mTexCoords = reference_mesh->mTexCoords;
-                mClothingWeights = reference_mesh->mClothingWeights;
-        }
-        else
-        {
-#if 1   // Allocate memory without initializing every vector
-                // NOTE: This makes asusmptions about the size of LLVector[234]
-                int nverts = mSharedData->mNumVertices;
-                int nfloats = nverts * (3*5 + 2 + 4);
-                mVertexData = new F32[nfloats];
-                int offset = 0;
-                mCoords =                               (LLVector3*)(mVertexData + offset); offset += 3*nverts;
-                mNormals =                              (LLVector3*)(mVertexData + offset); offset += 3*nverts;
-                mScaledNormals =                (LLVector3*)(mVertexData + offset); offset += 3*nverts;
-                mBinormals =                    (LLVector3*)(mVertexData + offset); offset += 3*nverts;
-                mScaledBinormals =              (LLVector3*)(mVertexData + offset); offset += 3*nverts;
-                mTexCoords =                    (LLVector2*)(mVertexData + offset); offset += 2*nverts;
-                mClothingWeights =      (LLVector4*)(mVertexData + offset); offset += 4*nverts;
-#else
-                mCoords = new LLVector3[mSharedData->mNumVertices];
-                mNormals = new LLVector3[mSharedData->mNumVertices];
-                mScaledNormals = new LLVector3[mSharedData->mNumVertices];
-                mBinormals = new LLVector3[mSharedData->mNumVertices];
-                mScaledBinormals = new LLVector3[mSharedData->mNumVertices];
-                mTexCoords = new LLVector2[mSharedData->mNumVertices];
-                mClothingWeights = new LLVector4[mSharedData->mNumVertices];
-                memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
-#endif
-                initializeForMorph();
-        }
+	LLMemType mt(LLMemType::MTYPE_AVATAR_MESH);
+
+	llassert(shared_data);
+
+	mSharedData = shared_data;
+	mReferenceMesh = reference_mesh;
+	mAvatarp = NULL;
+	mVertexData = NULL;
+
+	mCurVertexCount = 0;
+	mFaceIndexCount = 0;
+	mFaceIndexOffset = 0;
+	mFaceVertexCount = 0;
+	mFaceVertexOffset = 0;
+
+	if (shared_data->isLOD() && reference_mesh)
+	{
+		mCoords = reference_mesh->mCoords;
+		mNormals = reference_mesh->mNormals;
+		mScaledNormals = reference_mesh->mScaledNormals;
+		mBinormals = reference_mesh->mBinormals;
+		mScaledBinormals = reference_mesh->mScaledBinormals;
+		mTexCoords = reference_mesh->mTexCoords;
+		mClothingWeights = reference_mesh->mClothingWeights;
+	}
+	else
+	{
+		// Allocate memory without initializing every vector
+		// NOTE: This makes asusmptions about the size of LLVector[234]
+		int nverts = mSharedData->mNumVertices;
+		int nfloats = nverts * (2*4 + 3*3 + 2 + 4);
+		//use 16 byte aligned vertex data to make LLPolyMesh SSE friendly
+		mVertexData = (F32*) ll_aligned_malloc_16(nfloats*4);
+		int offset = 0;
+		mCoords				= 	(LLVector4*)(mVertexData + offset); offset += 4*nverts;
+		mNormals			=	(LLVector4*)(mVertexData + offset); offset += 4*nverts;
+		mClothingWeights	= 	(LLVector4*)(mVertexData + offset); offset += 4*nverts;
+		mTexCoords			= 	(LLVector2*)(mVertexData + offset); offset += 2*nverts;
+
+		// these members don't need to be 16-byte aligned, but the first one might be
+		// read during an aligned memcpy of mTexCoords
+		mScaledNormals =                (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+		mBinormals =                    (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+		mScaledBinormals =              (LLVector3*)(mVertexData + offset); offset += 3*nverts; 
+		initializeForMorph();
+	}
 }
 
 
@@ -803,17 +798,9 @@ LLPolyMesh::~LLPolyMesh()
                 delete mJointRenderData[i];
                 mJointRenderData[i] = NULL;
         }
-#if 0 // These are now allocated as one big uninitialized chunk
-        delete [] mCoords;
-        delete [] mNormals;
-        delete [] mScaledNormals;
-        delete [] mBinormals;
-        delete [] mScaledBinormals;
-        delete [] mClothingWeights;
-        delete [] mTexCoords;
-#else
-        delete [] mVertexData;
-#endif
+
+		ll_aligned_free_16(mVertexData);
+
 }
 
 
@@ -919,7 +906,7 @@ void LLPolyMesh::dumpDiagInfo()
 //-----------------------------------------------------------------------------
 // getWritableCoords()
 //-----------------------------------------------------------------------------
-LLVector3 *LLPolyMesh::getWritableCoords()
+LLVector4 *LLPolyMesh::getWritableCoords()
 {
         return mCoords;
 }
@@ -927,7 +914,7 @@ LLVector3 *LLPolyMesh::getWritableCoords()
 //-----------------------------------------------------------------------------
 // getWritableNormals()
 //-----------------------------------------------------------------------------
-LLVector3 *LLPolyMesh::getWritableNormals()
+LLVector4 *LLPolyMesh::getWritableNormals()
 {
         return mNormals;
 }
@@ -979,16 +966,17 @@ LLVector3 *LLPolyMesh::getScaledBinormals()
 //-----------------------------------------------------------------------------
 void LLPolyMesh::initializeForMorph()
 {
-        if (!mSharedData)
-                return;
-
-        memcpy(mCoords, mSharedData->mBaseCoords, sizeof(LLVector3) * mSharedData->mNumVertices);       /*Flawfinder: ignore*/
-        memcpy(mNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);     /*Flawfinder: ignore*/
-        memcpy(mScaledNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);       /*Flawfinder: ignore*/
-        memcpy(mBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices); /*Flawfinder: ignore*/
-        memcpy(mScaledBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);           /*Flawfinder: ignore*/
-        memcpy(mTexCoords, mSharedData->mTexCoords, sizeof(LLVector2) * mSharedData->mNumVertices);             /*Flawfinder: ignore*/
-        memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
+    for (U32 i = 0; i < mSharedData->mNumVertices; ++i)
+	{
+		mCoords[i] = LLVector4(mSharedData->mBaseCoords[i]);
+		mNormals[i] = LLVector4(mSharedData->mBaseNormals[i]);
+	}
+
+	memcpy(mScaledNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);	/*Flawfinder: ignore*/
+	memcpy(mBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);	/*Flawfinder: ignore*/
+	memcpy(mScaledBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);		/*Flawfinder: ignore*/
+	memcpy(mTexCoords, mSharedData->mTexCoords, sizeof(LLVector2) * mSharedData->mNumVertices);		/*Flawfinder: ignore*/
+	memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llpolymesh.h b/indra/newview/llpolymesh.h
index 894cd307c47ed9f6cbad9d0699947a3c8e0b680d..ba2bf85570379834083d498a7adef65c4cf6dee9 100644
--- a/indra/newview/llpolymesh.h
+++ b/indra/newview/llpolymesh.h
@@ -217,15 +217,15 @@ class LLPolyMesh
 	}
 
 	// Get coords
-	const LLVector3	*getCoords() const{
+	const LLVector4	*getCoords() const{
 		return mCoords;
 	}
 
 	// non const version
-	LLVector3 *getWritableCoords();
+	LLVector4 *getWritableCoords();
 
 	// Get normals
-	const LLVector3	*getNormals() const{ 
+	const LLVector4	*getNormals() const{ 
 		return mNormals; 
 	}
 
@@ -247,7 +247,7 @@ class LLPolyMesh
 	}
 
 	// intermediate morphed normals and output normals
-	LLVector3 *getWritableNormals();
+	LLVector4 *getWritableNormals();
 	LLVector3 *getScaledNormals();
 
 	LLVector3 *getWritableBinormals();
@@ -341,11 +341,11 @@ class LLPolyMesh
 	// Single array of floats for allocation / deletion
 	F32						*mVertexData;
 	// deformed vertices (resulting from application of morph targets)
-	LLVector3				*mCoords;
+	LLVector4				*mCoords;
 	// deformed normals (resulting from application of morph targets)
 	LLVector3				*mScaledNormals;
 	// output normals (after normalization)
-	LLVector3				*mNormals;
+	LLVector4				*mNormals;
 	// deformed binormals (resulting from application of morph targets)
 	LLVector3				*mScaledBinormals;
 	// output binormals (after normalization)
diff --git a/indra/newview/llpolymorph.cpp b/indra/newview/llpolymorph.cpp
index 36f8c8d13e49109c90a576294c1b162aba6be307..cefd7df3febf7d1496de7fa630aa53078e0adbe8 100644
--- a/indra/newview/llpolymorph.cpp
+++ b/indra/newview/llpolymorph.cpp
@@ -508,10 +508,10 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
 	if (delta_weight != 0.f)
 	{
 		llassert(!mMesh->isLOD());
-		LLVector3 *coords = mMesh->getWritableCoords();
+		LLVector4 *coords = mMesh->getWritableCoords();
 
 		LLVector3 *scaled_normals = mMesh->getScaledNormals();
-		LLVector3 *normals = mMesh->getWritableNormals();
+		LLVector4 *normals = mMesh->getWritableNormals();
 
 		LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
 		LLVector3 *binormals = mMesh->getWritableBinormals();
@@ -531,7 +531,8 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
 				maskWeight = maskWeightArray[vert_index_morph];
 			}
 
-			coords[vert_index_mesh] += mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
+			coords[vert_index_mesh] += LLVector4(mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight);
+
 			if (getInfo()->mIsClothingMorph && clothing_weights)
 			{
 				LLVector3 clothing_offset = mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
@@ -546,7 +547,7 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
 			scaled_normals[vert_index_mesh] += mMorphData->mNormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
 			LLVector3 normalized_normal = scaled_normals[vert_index_mesh];
 			normalized_normal.normVec();
-			normals[vert_index_mesh] = normalized_normal;
+			normals[vert_index_mesh] = LLVector4(normalized_normal);
 
 			// calculate new binormals
 			scaled_binormals[vert_index_mesh] += mMorphData->mBinormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
@@ -595,7 +596,7 @@ void	LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S3
 
 		if (maskWeights)
 		{
-			LLVector3 *coords = mMesh->getWritableCoords();
+			LLVector4 *coords = mMesh->getWritableCoords();
 			LLVector3 *scaled_normals = mMesh->getScaledNormals();
 			LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
 			LLVector2 *tex_coords = mMesh->getWritableTexCoords();
@@ -606,7 +607,7 @@ void	LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S3
 				S32 out_vert = mMorphData->mVertexIndices[vert];
 
 				// remove effect of existing masked morph
-				coords[out_vert] -= mMorphData->mCoords[vert] * lastMaskWeight;
+				coords[out_vert] -= LLVector4(mMorphData->mCoords[vert]) * lastMaskWeight;
 				scaled_normals[out_vert] -= mMorphData->mNormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
 				scaled_binormals[out_vert] -= mMorphData->mBinormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
 				tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * lastMaskWeight;
diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e8fc9dd25958ba55a18bdfe9328763e405f8fcc
--- /dev/null
+++ b/indra/newview/llsceneview.cpp
@@ -0,0 +1,429 @@
+/** 
+ * @file llsceneview.cpp
+ * @brief LLSceneView class implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsceneview.h"
+#include "llviewerwindow.h"
+#include "pipeline.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "llvolumemgr.h"
+
+LLSceneView* gSceneView = NULL;
+
+//borrow this helper function from llfasttimerview.cpp
+template <class VEC_TYPE>
+void removeOutliers(std::vector<VEC_TYPE>& data, F32 k);
+
+
+LLSceneView::LLSceneView(const LLRect& rect)
+	:	LLFloater(LLSD())
+{
+	setRect(rect);
+	setVisible(FALSE);
+	
+	setCanMinimize(false);
+	setCanClose(true);
+}
+
+void LLSceneView::onClickCloseBtn()
+{
+	setVisible(false);
+}
+
+
+void LLSceneView::draw()
+{
+	S32 margin = 10;
+	S32 height = (S32) (gViewerWindow->getWindowRectScaled().getHeight()*0.75f);
+	S32 width = (S32) (gViewerWindow->getWindowRectScaled().getWidth() * 0.75f);
+	
+	LLRect new_rect;
+	new_rect.setLeftTopAndSize(getRect().mLeft, getRect().mTop, width, height);
+	setRect(new_rect);
+
+	// Draw the window background
+	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+	gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
+	
+
+	//aggregate some statistics
+
+	//object sizes
+	std::vector<F32> size[2];
+
+	//triangle counts
+	std::vector<S32> triangles[2]; 
+	std::vector<S32> visible_triangles[2];
+	S32 total_visible_triangles[] = {0, 0};
+	S32 total_triangles[] = {0, 0};
+	
+	//streaming cost
+	std::vector<F32> streaming_cost[2];
+	F32 total_streaming[] = { 0.f, 0.f };
+
+	//physics cost
+	std::vector<F32> physics_cost[2];
+	F32 total_physics[] = { 0.f, 0.f };
+	
+
+	U32 object_count = 0;
+
+	LLViewerRegion* region = gAgent.getRegion();
+	if (region)
+	{
+		for (U32 i = 0; i < gObjectList.getNumObjects(); ++i)
+		{
+			LLViewerObject* object = gObjectList.getObject(i);
+			
+			if (object && 
+				object->getVolume()&&
+				object->getRegion() == region)
+			{
+				U32 idx = object->isAttachment() ? 1 : 0;
+
+				LLVolume* volume = object->getVolume();
+				object_count++;
+				
+				F32 radius = object->getScale().magVec();
+				size[idx].push_back(radius);
+
+				S32 visible = volume->getNumTriangles();
+				S32 high_triangles = object->getHighLODTriangleCount();
+
+				total_visible_triangles[idx] += visible;
+				total_triangles[idx] += high_triangles;
+
+				visible_triangles[idx].push_back(visible);
+				triangles[idx].push_back(high_triangles);
+
+				F32 streaming = object->getStreamingCost();
+				total_streaming[idx] += streaming;
+				streaming_cost[idx].push_back(streaming);
+
+				F32 physics = object->getPhysicsCost();
+				total_physics[idx] += physics;
+				physics_cost[idx].push_back(physics);
+			}
+		}
+	}
+
+	const char* category[] =
+	{
+		"Region",
+		"Attachment"
+	};
+
+	S32 graph_pos[4];
+
+	for (U32 i = 0; i < 4; ++i)
+	{
+		graph_pos[i] = new_rect.getHeight()/4*(i+1);
+	}
+
+	for (U32 idx = 0; idx < 2; idx++)
+	{
+		if (!size[idx].empty())
+		{ //display graph of object sizes
+			std::sort(size[idx].begin(), size[idx].end());
+
+			ll_remove_outliers(size[idx], 1.f);
+
+			LLRect size_rect;
+			
+			if (idx == 0)
+			{
+				size_rect = LLRect(margin, graph_pos[0]-margin, new_rect.getWidth()/2-margin, margin*2);
+			}
+			else
+			{
+				size_rect = LLRect(margin+new_rect.getWidth()/2, graph_pos[0]-margin, new_rect.getWidth()-margin, margin*2);
+			}
+
+			gl_rect_2d(size_rect, LLColor4::white, false);
+
+			F32 size_domain[] = { 128.f, 0.f };
+			
+			//get domain of sizes
+			for (U32 i = 0; i < size[idx].size(); ++i)
+			{
+				size_domain[0] = llmin(size_domain[0], size[idx][i]);
+				size_domain[1] = llmax(size_domain[1], size[idx][i]);
+			}
+
+			F32 size_range = size_domain[1]-size_domain[0];
+			
+			U32 count = size[idx].size();
+
+			F32 total = 0.f;
+
+			gGL.begin(LLRender::LINE_STRIP);
+
+			for (U32 i = 0; i < count; ++i)
+			{
+				F32 rad = size[idx][i];
+				total += rad;	
+				F32 y = (rad-size_domain[0])/size_range*size_rect.getHeight()+size_rect.mBottom;
+				F32 x = (F32) i / count * size_rect.getWidth() + size_rect.mLeft;
+
+				gGL.vertex2f(x,y);
+
+				if (i%4096 == 0)
+				{
+					gGL.end();
+					gGL.flush();
+					gGL.begin(LLRender::LINE_STRIP);
+				}
+			}
+
+			gGL.end();
+			gGL.flush();
+
+			std::string label = llformat("%s Object Sizes (m) -- [%.1f, %.1f] Mean: %.1f  Median: %.1f -- %d samples",
+											category[idx], size_domain[0], size_domain[1], total/count, size[idx][count/2], count);
+
+			LLFontGL::getFontMonospace()->renderUTF8(label,
+											0 , size_rect.mLeft, size_rect.mTop+margin, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+		}
+	}
+
+	for (U32 idx = 0; idx < 2; ++idx)
+	{
+		if (!triangles[idx].empty())
+		{ //plot graph of visible/total triangles
+			std::sort(triangles[idx].begin(), triangles[idx].end());
+			
+			ll_remove_outliers(triangles[idx], 1.f);
+			
+			LLRect tri_rect;
+			if (idx == 0)
+			{
+				tri_rect = LLRect(margin, graph_pos[1]-margin, new_rect.getWidth()/2-margin, graph_pos[0]+margin);
+			}
+			else
+			{
+				tri_rect = LLRect(new_rect.getWidth()/2+margin, graph_pos[1]-margin, new_rect.getWidth()-margin, graph_pos[0]+margin);
+			}
+
+			gl_rect_2d(tri_rect, LLColor4::white, false);
+
+			S32 tri_domain[] = { 65536, 0 };
+						
+			//get domain of triangle counts
+			for (U32 i = 0; i < triangles[idx].size(); ++i)
+			{
+				tri_domain[0] = llmin(tri_domain[0], triangles[idx][i]);
+				tri_domain[1] = llmax(tri_domain[1], triangles[idx][i]);		
+			}
+
+			U32 triangle_range = tri_domain[1]-tri_domain[0];
+
+			U32 count = triangles[idx].size();
+
+			U32 total = 0;
+
+			gGL.begin(LLRender::LINE_STRIP);
+			//plot triangles
+			for (U32 i = 0; i < count; ++i)
+			{
+				U32 tri_count = triangles[idx][i];
+				total += tri_count;	
+				F32 y = (F32) (tri_count-tri_domain[0])/triangle_range*tri_rect.getHeight()+tri_rect.mBottom;
+				F32 x = (F32) i / count * tri_rect.getWidth() + tri_rect.mLeft;
+
+				gGL.vertex2f(x,y);
+
+				if (i%4096 == 0)
+				{
+					gGL.end();
+					gGL.flush();
+					gGL.begin(LLRender::LINE_STRIP);
+				}
+			}
+
+			gGL.end();
+			gGL.flush();
+
+			U32 total_visible = 0;
+			count = visible_triangles[idx].size();
+
+			for (U32 i = 0; i < count; ++i)
+			{
+				U32 tri_count = visible_triangles[idx][i];
+				total_visible += tri_count;	
+			}
+
+			std::string label = llformat("%s Object Triangle Counts (Ktris) -- [%.2f, %.2f] Mean: %.2f  Median: %.2f  Visible: %.2f/%.2f",
+											category[idx], tri_domain[0]/1024.f, tri_domain[1]/1024.f, (total/count)/1024.f, triangles[idx][count/2]/1024.f, total_visible_triangles[idx]/1024.f, total_triangles[idx]/1024.f);
+
+			LLFontGL::getFontMonospace()->renderUTF8(label,
+											0 , tri_rect.mLeft, tri_rect.mTop+margin, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+		}
+	}
+
+	for (U32 idx = 0; idx < 2; ++idx)
+	{
+		if (!streaming_cost[idx].empty())
+		{ //plot graph of streaming cost
+			std::sort(streaming_cost[idx].begin(), streaming_cost[idx].end());
+			
+			ll_remove_outliers(streaming_cost[idx], 1.f);
+			
+			LLRect tri_rect;
+			if (idx == 0)
+			{
+				tri_rect = LLRect(margin, graph_pos[2]-margin, new_rect.getWidth()/2-margin, graph_pos[1]+margin);
+			}
+			else
+			{
+				tri_rect = LLRect(new_rect.getWidth()/2+margin, graph_pos[2]-margin, new_rect.getWidth()-margin, graph_pos[1]+margin);
+			}
+
+			gl_rect_2d(tri_rect, LLColor4::white, false);
+
+			F32 streaming_domain[] = { 65536, 0 };
+						
+			//get domain of triangle counts
+			for (U32 i = 0; i < streaming_cost[idx].size(); ++i)
+			{
+				streaming_domain[0] = llmin(streaming_domain[0], streaming_cost[idx][i]);
+				streaming_domain[1] = llmax(streaming_domain[1], streaming_cost[idx][i]);		
+			}
+
+			F32 cost_range = streaming_domain[1]-streaming_domain[0];
+
+			U32 count = streaming_cost[idx].size();
+
+			F32 total = 0;
+
+			gGL.begin(LLRender::LINE_STRIP);
+			//plot triangles
+			for (U32 i = 0; i < count; ++i)
+			{
+				F32 sc = streaming_cost[idx][i];
+				total += sc;	
+				F32 y = (F32) (sc-streaming_domain[0])/cost_range*tri_rect.getHeight()+tri_rect.mBottom;
+				F32 x = (F32) i / count * tri_rect.getWidth() + tri_rect.mLeft;
+
+				gGL.vertex2f(x,y);
+
+				if (i%4096 == 0)
+				{
+					gGL.end();
+					gGL.flush();
+					gGL.begin(LLRender::LINE_STRIP);
+				}
+			}
+
+			gGL.end();
+			gGL.flush();
+
+			std::string label = llformat("%s Object Streaming Cost -- [%.2f, %.2f] Mean: %.2f  Total: %.2f",
+											category[idx], streaming_domain[0], streaming_domain[1], total/count, total_streaming[idx]);
+
+			LLFontGL::getFontMonospace()->renderUTF8(label,
+											0 , tri_rect.mLeft, tri_rect.mTop+margin, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+		}
+	}
+
+	for (U32 idx = 0; idx < 2; ++idx)
+	{
+		if (!physics_cost[idx].empty())
+		{ //plot graph of physics cost
+			std::sort(physics_cost[idx].begin(), physics_cost[idx].end());
+			
+			ll_remove_outliers(physics_cost[idx], 1.f);
+			
+			LLRect tri_rect;
+			if (idx == 0)
+			{
+				tri_rect = LLRect(margin, graph_pos[3]-margin, new_rect.getWidth()/2-margin, graph_pos[2]+margin);
+			}
+			else
+			{
+				tri_rect = LLRect(new_rect.getWidth()/2+margin, graph_pos[3]-margin, new_rect.getWidth()-margin, graph_pos[2]+margin);
+			}
+
+			gl_rect_2d(tri_rect, LLColor4::white, false);
+
+			F32 physics_domain[] = { 65536, 0 };
+						
+			//get domain of triangle counts
+			for (U32 i = 0; i < physics_cost[idx].size(); ++i)
+			{
+				physics_domain[0] = llmin(physics_domain[0], physics_cost[idx][i]);
+				physics_domain[1] = llmax(physics_domain[1], physics_cost[idx][i]);		
+			}
+
+			F32 cost_range = physics_domain[1]-physics_domain[0];
+
+			U32 count = physics_cost[idx].size();
+
+			F32 total = 0;
+
+			gGL.begin(LLRender::LINE_STRIP);
+			//plot triangles
+			for (U32 i = 0; i < count; ++i)
+			{
+				F32 pc = physics_cost[idx][i];
+				total += pc;	
+				F32 y = (F32) (pc-physics_domain[0])/cost_range*tri_rect.getHeight()+tri_rect.mBottom;
+				F32 x = (F32) i / count * tri_rect.getWidth() + tri_rect.mLeft;
+
+				gGL.vertex2f(x,y);
+
+				if (i%4096 == 0)
+				{
+					gGL.end();
+					gGL.flush();
+					gGL.begin(LLRender::LINE_STRIP);
+				}
+			}
+
+			gGL.end();
+			gGL.flush();
+
+			std::string label = llformat("%s Object Physics Cost -- [%.2f, %.2f] Mean: %.2f  Total: %.2f",
+											category[idx], physics_domain[0], physics_domain[1], total/count, total_physics[idx]);
+
+			LLFontGL::getFontMonospace()->renderUTF8(label,
+											0 , tri_rect.mLeft, tri_rect.mTop+margin, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+		}
+	}
+
+	
+	
+
+	LLView::draw();
+}
+
+
diff --git a/indra/newview/llsceneview.h b/indra/newview/llsceneview.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a3a14bbee45571dd6dee59bc3ab2633dfa7a380
--- /dev/null
+++ b/indra/newview/llsceneview.h
@@ -0,0 +1,48 @@
+/** 
+ * @file llsceneview.h
+ * @brief LLSceneView class header file
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSCENEVIEW_H
+#define LL_LLSCENEVIEW_H
+
+#include "llfloater.h"
+
+
+class LLSceneView : public LLFloater
+{
+public:
+	LLSceneView(const LLRect& rect);
+
+	virtual void draw();
+	
+protected:
+	virtual void onClickCloseBtn();
+
+
+};
+
+extern LLSceneView* gSceneView;
+
+#endif // LL_LLSCENEVIEW_H
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index f05892d9b0943524997e3577b93600b9aa32c749..9b264b81c782dfd6ba9b75ae68713e8e81e1062a 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -64,6 +64,7 @@
 #include "llhudmanager.h"
 #include "llinventorymodel.h"
 #include "llmenugl.h"
+#include "llmeshrepository.h"
 #include "llmutelist.h"
 #include "llnotificationsutil.h"
 #include "llsidepaneltaskinfo.h"
@@ -176,7 +177,6 @@ LLObjectSelection *get_null_object_selection()
 
 // Build time optimization, generate this function once here
 template class LLSelectMgr* LLSingleton<class LLSelectMgr>::getInstance();
-
 //-----------------------------------------------------------------------------
 // LLSelectMgr()
 //-----------------------------------------------------------------------------
@@ -1191,8 +1191,8 @@ void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &
 		mGridRotation = first_grid_object->getRenderRotation();
 		LLVector3 first_grid_obj_pos = first_grid_object->getRenderPosition();
 
-		LLVector3 min_extents(F32_MAX, F32_MAX, F32_MAX);
-		LLVector3 max_extents(-F32_MAX, -F32_MAX, -F32_MAX);
+		LLVector4a min_extents(F32_MAX);
+		LLVector4a max_extents(-F32_MAX);
 		BOOL grid_changed = FALSE;
 		for (LLObjectSelection::iterator iter = mGridObjects.begin();
 			 iter != mGridObjects.end(); ++iter)
@@ -1201,7 +1201,7 @@ void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &
 			LLDrawable* drawable = object->mDrawable;
 			if (drawable)
 			{
-				const LLVector3* ext = drawable->getSpatialExtents();
+				const LLVector4a* ext = drawable->getSpatialExtents();
 				update_min_max(min_extents, max_extents, ext[0]);
 				update_min_max(min_extents, max_extents, ext[1]);
 				grid_changed = TRUE;
@@ -1209,13 +1209,19 @@ void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &
 		}
 		if (grid_changed)
 		{
-			mGridOrigin = lerp(min_extents, max_extents, 0.5f);
+			LLVector4a center, size;
+			center.setAdd(min_extents, max_extents);
+			center.mul(0.5f);
+			size.setSub(max_extents, min_extents);
+			size.mul(0.5f);
+
+			mGridOrigin.set(center.getF32ptr());
 			LLDrawable* drawable = first_grid_object->mDrawable;
 			if (drawable && drawable->isActive())
 			{
 				mGridOrigin = mGridOrigin * first_grid_object->getRenderMatrix();
 			}
-			mGridScale = (max_extents - min_extents) * 0.5f;
+			mGridScale.set(size.getF32ptr());
 		}
 	}
 	else // GRID_MODE_WORLD or just plain default
@@ -1979,6 +1985,103 @@ BOOL LLSelectMgr::selectionGetGlow(F32 *glow)
 	return identical;
 }
 
+
+void LLSelectMgr::selectionSetPhysicsType(U8 type)
+{
+	struct f : public LLSelectedObjectFunctor
+	{
+		U8 mType;
+		f(const U8& t) : mType(t) {}
+		virtual bool apply(LLViewerObject* object)
+		{
+			if (object->permModify())
+			{
+				object->setPhysicsShapeType(mType);
+				object->updateFlags();
+			}
+			return true;
+		}
+	} sendfunc(type);
+	getSelection()->applyToObjects(&sendfunc);
+}
+
+void LLSelectMgr::selectionSetFriction(F32 friction)
+{
+	struct f : public LLSelectedObjectFunctor
+	{
+		F32 mFriction;
+		f(const F32& friction) : mFriction(friction) {}
+		virtual bool apply(LLViewerObject* object)
+		{
+			if (object->permModify())
+			{
+				object->setPhysicsFriction(mFriction);
+				object->updateFlags();
+			}
+			return true;
+		}
+	} sendfunc(friction);
+	getSelection()->applyToObjects(&sendfunc);
+}
+
+void LLSelectMgr::selectionSetGravity(F32 gravity )
+{
+	struct f : public LLSelectedObjectFunctor
+	{
+		F32 mGravity;
+		f(const F32& gravity) : mGravity(gravity) {}
+		virtual bool apply(LLViewerObject* object)
+		{
+			if (object->permModify())
+			{
+				object->setPhysicsGravity(mGravity);
+				object->updateFlags();
+			}
+			return true;
+		}
+	} sendfunc(gravity);
+	getSelection()->applyToObjects(&sendfunc);
+}
+
+void LLSelectMgr::selectionSetDensity(F32 density )
+{
+	struct f : public LLSelectedObjectFunctor
+	{
+		F32 mDensity;
+		f(const F32& density ) : mDensity(density) {}
+		virtual bool apply(LLViewerObject* object)
+		{
+			if (object->permModify())
+			{
+				object->setPhysicsDensity(mDensity);
+				object->updateFlags();
+			}
+			return true;
+		}
+	} sendfunc(density);
+	getSelection()->applyToObjects(&sendfunc);
+}
+
+void LLSelectMgr::selectionSetRestitution(F32 restitution)
+{
+	struct f : public LLSelectedObjectFunctor
+	{
+		F32 mRestitution;
+		f(const F32& restitution ) : mRestitution(restitution) {}
+		virtual bool apply(LLViewerObject* object)
+		{
+			if (object->permModify())
+			{
+				object->setPhysicsRestitution(mRestitution);
+				object->updateFlags();
+			}
+			return true;
+		}
+	} sendfunc(restitution);
+	getSelection()->applyToObjects(&sendfunc);
+}
+
+
 //-----------------------------------------------------------------------------
 // selectionSetMaterial()
 //-----------------------------------------------------------------------------
@@ -3628,7 +3731,7 @@ void LLSelectMgr::deselectAllIfTooFar()
 		{
 			if (mDebugSelectMgr)
 			{
-				llinfos << "Selection manager: auto-deselecting, select_dist = " << fsqrtf(select_dist_sq) << llendl;
+				llinfos << "Selection manager: auto-deselecting, select_dist = " << (F32) sqrt(select_dist_sq) << llendl;
 				llinfos << "agent pos global = " << gAgent.getPositionGlobal() << llendl;
 				llinfos << "selection pos global = " << selectionCenter << llendl;
 			}
@@ -3797,6 +3900,26 @@ void LLSelectMgr::sendDelink()
 		return;
 	}
 
+	struct f : public LLSelectedObjectFunctor
+	{ //on delink, any modifyable object should
+		f() {}
+
+		virtual bool apply(LLViewerObject* object)
+		{
+			if (object->permModify())
+			{
+				if (object->getPhysicsShapeType() == LLViewerObject::PHYSICS_SHAPE_NONE)
+				{
+					object->setPhysicsShapeType(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+					object->updateFlags();
+				}
+			}
+			return true;
+		}
+	} sendfunc;
+	getSelection()->applyToObjects(&sendfunc);
+
+
 	// Delink needs to send individuals so you can unlink a single object from
 	// a linked set.
 	sendListToRegions(
@@ -4026,7 +4149,6 @@ void LLSelectMgr::selectionUpdateCastShadows(BOOL cast_shadows)
 	getSelection()->applyToObjects(&func);	
 }
 
-
 //----------------------------------------------------------------------
 // Helpful packing functions for sendObjectMessage()
 //----------------------------------------------------------------------
@@ -4715,7 +4837,6 @@ void LLSelectMgr::processForceObjectSelect(LLMessageSystem* msg, void**)
 	LLSelectMgr::getInstance()->highlightObjectAndFamily(objects);
 }
 
-
 extern LLGLdouble	gGLModelView[16];
 
 void LLSelectMgr::updateSilhouettes()
@@ -5199,7 +5320,6 @@ LLSelectNode::LLSelectNode(const LLSelectNode& nodep)
 
 	mSilhouetteVertices = nodep.mSilhouetteVertices;
 	mSilhouetteNormals = nodep.mSilhouetteNormals;
-	mSilhouetteSegments = nodep.mSilhouetteSegments;
 	mSilhouetteExists = nodep.mSilhouetteExists;
 	mObject = nodep.mObject;
 
@@ -5433,6 +5553,111 @@ BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power)
 	return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id));
 }
 
+
+//helper function for pushing relevant vertices from drawable to GL
+void pushWireframe(LLDrawable* drawable)
+{
+	if (drawable->isState(LLDrawable::RIGGED))
+	{ //render straight from rigged volume if this is a rigged attachment
+		LLVOVolume* vobj = drawable->getVOVolume();
+		if (vobj)
+		{
+			vobj->updateRiggedVolume();
+			LLRiggedVolume* rigged_volume = vobj->getRiggedVolume();
+			if (rigged_volume)
+			{
+				LLVertexBuffer::unbind();
+				gGL.pushMatrix();
+				glMultMatrixf((F32*) vobj->getRelativeXform().mMatrix);
+				for (S32 i = 0; i < rigged_volume->getNumVolumeFaces(); ++i)
+				{
+					const LLVolumeFace& face = rigged_volume->getVolumeFace(i);
+					glVertexPointer(3, GL_FLOAT, 16, face.mPositions);
+					glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
+				}
+				gGL.popMatrix();
+			}
+		}
+	}
+	else
+	{
+		for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+		{
+			LLFace* face = drawable->getFace(i);
+			if (face->verify())
+			{
+				pushVerts(face, LLVertexBuffer::MAP_VERTEX);
+			}
+		}
+	}
+}
+
+void LLSelectNode::renderOneWireframe(const LLColor4& color)
+{
+	LLViewerObject* objectp = getObject();
+	if (!objectp)
+	{
+		return;
+	}
+
+	LLDrawable* drawable = objectp->mDrawable;
+	if(!drawable)
+	{
+		return;
+	}
+
+	glMatrixMode(GL_MODELVIEW);
+	gGL.pushMatrix();
+	
+	BOOL is_hud_object = objectp->isHUDAttachment();
+
+	if (drawable->isActive())
+	{
+		glLoadMatrixd(gGLModelView);
+		glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix);
+	}
+	else if (!is_hud_object)
+	{
+		glLoadIdentity();
+		glMultMatrixd(gGLModelView);
+		LLVector3 trans = objectp->getRegion()->getOriginAgent();		
+		glTranslatef(trans.mV[0], trans.mV[1], trans.mV[2]);		
+	}
+	
+	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+	
+	if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible())
+	{
+		gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE);
+		LLGLEnable fog(GL_FOG);
+		glFogi(GL_FOG_MODE, GL_LINEAR);
+		float d = (LLViewerCamera::getInstance()->getPointOfInterest()-LLViewerCamera::getInstance()->getOrigin()).magVec();
+		LLColor4 fogCol = color * (F32)llclamp((LLSelectMgr::getInstance()->getSelectionCenterGlobal()-gAgentCamera.getCameraPositionGlobal()).magVec()/(LLSelectMgr::getInstance()->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0);
+		glFogf(GL_FOG_START, d);
+		glFogf(GL_FOG_END, d*(1 + (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV())));
+		glFogfv(GL_FOG_COLOR, fogCol.mV);
+
+		LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL);
+		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
+		{
+			glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
+			pushWireframe(drawable);
+		}
+	}
+
+	gGL.flush();
+	gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+	glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+	LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
+	glPolygonOffset(3.f, 3.f);
+	glLineWidth(3.f);
+	pushWireframe(drawable);
+	glLineWidth(1.f);
+	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+	gGL.popMatrix();
+}
+
 //-----------------------------------------------------------------------------
 // renderOneSilhouette()
 //-----------------------------------------------------------------------------
@@ -5450,6 +5675,13 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 		return;
 	}
 
+	LLVOVolume* vobj = drawable->getVOVolume();
+	if (vobj && vobj->isMesh())
+	{
+		renderOneWireframe(color);
+		return;
+	}
+
 	if (!mSilhouetteExists)
 	{
 		return;
@@ -5514,17 +5746,15 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 			gGL.begin(LLRender::LINES);
 			{
-				S32 i = 0;
-				for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
+				for(S32 i = 0; i < mSilhouetteVertices.size(); i += 2)
 				{
-					for(; i < mSilhouetteSegments[seg_num]; i++)
-					{
-						u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
-
-						gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
-						gGL.texCoord2f( u_coord, v_coord );
-						gGL.vertex3fv( mSilhouetteVertices[i].mV );
-					}
+					u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+					gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
+					gGL.texCoord2f( u_coord, v_coord );
+					gGL.vertex3fv( mSilhouetteVertices[i].mV);
+					u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+					gGL.texCoord2f( u_coord, v_coord );
+					gGL.vertex3fv(mSilhouetteVertices[i+1].mV);
 				}
 			}
             gGL.end();
@@ -5535,51 +5765,50 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 		gGL.setSceneBlendType(LLRender::BT_ALPHA);
 		gGL.begin(LLRender::TRIANGLES);
 		{
-			S32 i = 0;
-			for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
+			for(S32 i = 0; i < mSilhouetteVertices.size(); i+=2)
 			{
-				S32 first_i = i;
-				LLVector3 v;
-				LLVector2 t;
+				if (!mSilhouetteNormals[i].isFinite() ||
+					!mSilhouetteNormals[i+1].isFinite())
+				{ //skip skewed segments
+					continue;
+				}
 
-				for(; i < mSilhouetteSegments[seg_num]; i++)
-				{
+				LLVector3 v[4];
+				LLVector2 tc[4];
+				v[0] = mSilhouetteVertices[i] + (mSilhouetteNormals[i] * silhouette_thickness);
+				tc[0].set(u_coord, v_coord + LLSelectMgr::sHighlightVScale);
 
-					if (i == first_i) {
-					    LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness;
-						vert += mSilhouetteVertices[i];
+				v[1] = mSilhouetteVertices[i];
+				tc[1].set(u_coord, v_coord);
 
-						gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
-						gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale );
-						gGL.vertex3fv( vert.mV ); 
-						
-						u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+				u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
 
-						gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
-						gGL.texCoord2f( u_coord, v_coord );
-						gGL.vertex3fv( mSilhouetteVertices[i].mV );
+				v[2] = mSilhouetteVertices[i+1] + (mSilhouetteNormals[i+1] * silhouette_thickness);
+				tc[2].set(u_coord, v_coord + LLSelectMgr::sHighlightVScale);
+				
+				v[3] = mSilhouetteVertices[i+1];
+				tc[3].set(u_coord,v_coord);
 
-						v = mSilhouetteVertices[i];
-						t = LLVector2(u_coord, v_coord);
-					}
-					else {
-                        LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness;
-						vert += mSilhouetteVertices[i];
-
-						gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
-						gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale );
-						gGL.vertex3fv( vert.mV ); 
-						gGL.vertex3fv( vert.mV ); 
-						
-						gGL.texCoord2fv(t.mV);
-						u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
-						gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
-						gGL.vertex3fv(v.mV);
-						gGL.texCoord2f( u_coord, v_coord );
-						gGL.vertex3fv( mSilhouetteVertices[i].mV );
+				gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
+				gGL.texCoord2fv(tc[0].mV);
+				gGL.vertex3fv( v[0].mV ); 
+				
+				gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+				gGL.texCoord2fv( tc[1].mV );
+				gGL.vertex3fv( v[1].mV );
 
-					}
-				}
+				gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
+				gGL.texCoord2fv( tc[2].mV );
+				gGL.vertex3fv( v[2].mV );
+
+				gGL.vertex3fv( v[2].mV );
+
+				gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+				gGL.texCoord2fv( tc[1].mV );
+				gGL.vertex3fv( v[1].mV );
+
+				gGL.texCoord2fv( tc[3].mV );
+				gGL.vertex3fv( v[3].mV );			
 			}
 		}
 		gGL.end();
@@ -6147,9 +6376,178 @@ S32 LLObjectSelection::getObjectCount()
 {
 	cleanupNodes();
 	S32 count = mList.size();
+
+	return count;
+}
+
+F32 LLObjectSelection::getSelectedObjectCost()
+{
+	cleanupNodes();
+	F32 cost = 0.f;
+
+	for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+	{
+		LLSelectNode* node = *iter;
+		LLViewerObject* object = node->getObject();
+		
+		if (object)
+		{
+			cost += object->getObjectCost();
+		}
+	}
+
+	return cost;
+}
+
+F32 LLObjectSelection::getSelectedLinksetCost()
+{
+	cleanupNodes();
+	F32 cost = 0.f;
+
+	std::set<LLViewerObject*> me_roots;
+
+	for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+	{
+		LLSelectNode* node = *iter;
+		LLViewerObject* object = node->getObject();
+		
+		if (object)
+		{
+			LLViewerObject* root = static_cast<LLViewerObject*>(object->getRoot());
+			if (root)
+			{
+				if (me_roots.find(root) == me_roots.end())
+				{
+					me_roots.insert(root);
+					cost += root->getLinksetCost();
+				}
+			}
+		}
+	}
+
+	return cost;
+}
+
+F32 LLObjectSelection::getSelectedPhysicsCost()
+{
+	cleanupNodes();
+	F32 cost = 0.f;
+
+	for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+	{
+		LLSelectNode* node = *iter;
+		LLViewerObject* object = node->getObject();
+		
+		if (object)
+		{
+			cost += object->getPhysicsCost();
+		}
+	}
+
+	return cost;
+}
+
+F32 LLObjectSelection::getSelectedLinksetPhysicsCost()
+{
+	cleanupNodes();
+	F32 cost = 0.f;
+
+	std::set<LLViewerObject*> me_roots;
+
+	for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+	{
+		LLSelectNode* node = *iter;
+		LLViewerObject* object = node->getObject();
+		
+		if (object)
+		{
+			LLViewerObject* root = static_cast<LLViewerObject*>(object->getRoot());
+			if (root)
+			{
+				if (me_roots.find(root) == me_roots.end())
+				{
+					me_roots.insert(root);
+					cost += root->getLinksetPhysicsCost();
+				}
+			}
+		}
+	}
+
+	return cost;
+}
+
+F32 LLObjectSelection::getSelectedObjectStreamingCost(S32* total_bytes, S32* visible_bytes)
+{
+	F32 cost = 0.f;
+	for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+	{
+		LLSelectNode* node = *iter;
+		LLViewerObject* object = node->getObject();
+		
+		if (object)
+		{
+			S32 bytes = 0;
+			S32 visible = 0;
+			cost += object->getStreamingCost(&bytes, &visible);
+
+			if (total_bytes)
+			{
+				*total_bytes += bytes;
+			}
+
+			if (visible_bytes)
+			{
+				*visible_bytes += visible;
+			}
+		}
+	}
+
+	return cost;
+}
+
+U32 LLObjectSelection::getSelectedObjectTriangleCount()
+{
+	U32 count = 0;
+	for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+	{
+		LLSelectNode* node = *iter;
+		LLViewerObject* object = node->getObject();
+		
+		if (object)
+		{
+			count += object->getTriangleCount();
+		}
+	}
+
 	return count;
 }
 
+/*S32 LLObjectSelection::getSelectedObjectRenderCost()
+{
+       S32 cost = 0;
+       LLVOVolume::texture_cost_t textures;
+       for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+       {
+               LLSelectNode* node = *iter;
+               LLVOVolume* object = (LLVOVolume*)node->getObject();
+
+               if (object)
+               {
+                       cost += object->getRenderCost(textures);
+               }
+
+               for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+               {
+                       // add the cost of each individual texture in the linkset
+                       cost += iter->second;
+               }
+               textures.clear();
+       }
+
+
+       return cost;
+}*/
+
 
 //-----------------------------------------------------------------------------
 // getTECount()
@@ -6585,7 +6983,7 @@ bool LLSelectMgr::selectionMove(const LLVector3& displ,
 		
 		// factor the distance into the displacement vector. This will get us
 		// equally visible movements for both close and far away selections.
-		F32 min_dist = sqrt(fsqrtf(min_dist_squared)) / 2;
+		F32 min_dist = sqrt((F32) sqrtf(min_dist_squared)) / 2;
 		displ_global.setVec(displ.mV[0] * min_dist,
 							displ.mV[1] * min_dist,
 							displ.mV[2] * min_dist);
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
index cb387f5c3c470e3518b14970df16e09b008b34c2..166616e13e03e96ee1716ae54bcead366b0bca1e 100644
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -134,6 +134,7 @@ class LLSelectNode
 	BOOL isTESelected(S32 te_index);
 	S32 getLastSelectedTE();
 	S32 getTESelectMask() { return mTESelectMask; }
+	void renderOneWireframe(const LLColor4& color);
 	void renderOneSilhouette(const LLColor4 &color);
 	void setTransient(BOOL transient) { mTransient = transient; }
 	BOOL isTransient() { return mTransient; }
@@ -181,7 +182,6 @@ class LLSelectNode
 	std::vector<LLVector3>  mTextureScaleRatios;
 	std::vector<LLVector3>	mSilhouetteVertices;	// array of vertices to render silhouette of object
 	std::vector<LLVector3>	mSilhouetteNormals;	// array of normals to render silhouette of object
-	std::vector<S32>		mSilhouetteSegments;	// array of normals to render silhouette of object
 	BOOL					mSilhouetteExists;	// need to generate silhouette?
 
 protected:
@@ -279,6 +279,15 @@ class LLObjectSelection : public LLRefCount
 
 	// count members
 	S32 getObjectCount();
+	F32 getSelectedObjectCost();
+	F32 getSelectedLinksetCost();
+	F32 getSelectedPhysicsCost();
+	F32 getSelectedLinksetPhysicsCost();
+	S32 getSelectedObjectRenderCost();
+	
+	F32 getSelectedObjectStreamingCost(S32* total_bytes = NULL, S32* visible_bytes = NULL);
+	U32 getSelectedObjectTriangleCount();
+
 	S32 getTECount();
 	S32 getRootObjectCount();
 
@@ -500,6 +509,11 @@ class LLSelectMgr : public LLEditMenuHandler, public LLSingleton<LLSelectMgr>
 	bool selectionGetIncludeInSearch(bool* include_in_search_out); // true if all selected objects have same
 	BOOL selectionGetGlow(F32 *glow);
 
+	void selectionSetPhysicsType(U8 type);
+	void selectionSetGravity(F32 gravity);
+	void selectionSetFriction(F32 friction);
+	void selectionSetDensity(F32 density);
+	void selectionSetRestitution(F32 restitution);
 	void selectionSetMaterial(U8 material);
 	void selectionSetImage(const LLUUID& imageid); // could be item or asset id
 	void selectionSetColor(const LLColor4 &color);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 94784f3f4912462f95a98efcc9c0be21c205b318..fa329eb0ae0271807132e0a46f576214dd5ebbd3 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -32,15 +32,19 @@
 #include "llviewerobjectlist.h"
 #include "llvovolume.h"
 #include "llvolume.h"
+#include "llvolumeoctree.h"
 #include "llviewercamera.h"
 #include "llface.h"
 #include "llviewercontrol.h"
 #include "llviewerregion.h"
 #include "llcamera.h"
 #include "pipeline.h"
+#include "llmeshrepository.h"
 #include "llrender.h"
 #include "lloctree.h"
+#include "llphysicsshapebuilderutil.h"
 #include "llvoavatar.h"
+#include "llvolumemgr.h"
 #include "lltextureatlas.h"
 
 static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling");
@@ -60,6 +64,12 @@ const F32 SG_OCCLUSION_FUDGE = 0.25f;
 
 static U32 sZombieGroups = 0;
 U32 LLSpatialGroup::sNodeCount = 0;
+
+#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
+
+std::set<GLuint> LLSpatialGroup::sPendingQueries;
+
+
 BOOL LLSpatialGroup::sNoDelete = FALSE;
 
 static F32 sLastMaxTexPriority = 1.f;
@@ -77,6 +87,9 @@ class LLOcclusionQueryPool : public LLGLNamePool
 
 	virtual void releaseName(GLuint name)
 	{
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+		LLSpatialGroup::sPendingQueries.erase(name);
+#endif
 		glDeleteQueriesARB(1, &name);
 	}
 };
@@ -95,23 +108,6 @@ void sg_assert(BOOL expr)
 #endif
 }
 
-#if LL_DEBUG
-void validate_drawable(LLDrawable* drawablep)
-{
-	F64 rad = drawablep->getBinRadius();
-	const LLVector3* ext = drawablep->getSpatialExtents();
-
-	if (rad < 0 || rad > 4096 ||
-		(ext[1]-ext[0]).magVec() > 4096)
-	{
-		llwarns << "Invalid drawable found in octree." << llendl;
-	}
-}
-#else
-#define validate_drawable(x)
-#endif
-
-
 S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad)
 {
 	return AABBSphereIntersectR2(min, max, origin, rad*rad);
@@ -151,6 +147,55 @@ S32 AABBSphereIntersectR2(const LLVector3& min, const LLVector3& max, const LLVe
 }
 
 
+S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad)
+{
+	return AABBSphereIntersectR2(min, max, origin, rad*rad);
+}
+
+S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &r)
+{
+	F32 d = 0.f;
+	F32 t;
+	
+	LLVector4a origina;
+	origina.load3(origin.mV);
+
+	LLVector4a v;
+	v.setSub(min, origina);
+	
+	if (v.dot3(v) < r)
+	{
+		v.setSub(max, origina);
+		if (v.dot3(v) < r)
+		{
+			return 2;
+		}
+	}
+
+
+	for (U32 i = 0; i < 3; i++)
+	{
+		if (origin.mV[i] < min[i])
+		{
+			t = min[i] - origin.mV[i];
+			d += t*t;
+		}
+		else if (origin.mV[i] > max[i])
+		{
+			t = origin.mV[i] - max[i];
+			d += t*t;
+		}
+
+		if (d > r)
+		{
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
 typedef enum
 {
 	b000 = 0x00,
@@ -168,76 +213,113 @@ typedef enum
 //gives you a triangle fan index array
 static U8 sOcclusionIndices[] =
 {
-	 // 000
+	 //000
 		b111, b110, b010, b011, b001, b101, b100, b110,
-	//001 
-		b110, b000, b010, b011, b111, b101, b100, b000,
+	 //001 
+		b011, b010, b000, b001, b101, b111, b110, b010,
 	 //010
 		b101, b100, b110, b111, b011, b001, b000, b100,
 	 //011 
-		b100, b010, b110, b111, b101, b001, b000, b010,
-	//100 
-		b011, b010, b000, b001, b101, b111, b110, b010,
+		b001, b000, b100, b101, b111, b011, b010, b000,
+	 //100 
+		b110, b000, b010, b011, b111, b101, b100, b000,
 	 //101 
 		b010, b100, b000, b001, b011, b111, b110, b100,
 	 //110
-		b001, b000, b100, b101, b111, b011, b010, b000,
+		b100, b010, b110, b111, b101, b001, b000, b010,
 	 //111
 		b000, b110, b100, b101, b001, b011, b010, b110,
 };
 
-U8* get_box_fan_indices(LLCamera* camera, const LLVector3& center)
+U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
 {
-	LLVector3 d = center - camera->getOrigin();
+	LLVector4a origin;
+	origin.load3(camera->getOrigin().mV);
 
-	U8 cypher = 0;
-	if (d.mV[0] > 0)
-	{
-		cypher |= b100;
-	}
-	if (d.mV[1] > 0)
-	{
-		cypher |= b010;
-	}
-	if (d.mV[2] > 0)
-	{
-		cypher |= b001;
-	}
+	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
+	
+	return cypher*8;
+}
+
+U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
+{
+	LLVector4a origin;
+	origin.load3(camera->getOrigin().mV);
 
+	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
+	
 	return sOcclusionIndices+cypher*8;
 }
-						
+
+
+static LLFastTimer::DeclareTimer FTM_BUILD_OCCLUSION("Build Occlusion");
+
 void LLSpatialGroup::buildOcclusion()
 {
-	if (!mOcclusionVerts)
+	if (mOcclusionVerts.isNull())
 	{
-		mOcclusionVerts = new F32[8*3];
+
+		mOcclusionVerts = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX, 
+			LLVertexBuffer::sUseStreamDraw ? mBufferUsage : 0); //if GL has a hard time with VBOs, don't use them for occlusion culling.
+		mOcclusionVerts->allocateBuffer(8, 64, true);
+	
+		LLStrider<U16> idx;
+		mOcclusionVerts->getIndexStrider(idx);
+		for (U32 i = 0; i < 64; i++)
+		{
+			*idx++ = sOcclusionIndices[i];
+		}
 	}
 
-	LLVector3 r = mBounds[1] + LLVector3(SG_OCCLUSION_FUDGE, SG_OCCLUSION_FUDGE, SG_OCCLUSION_FUDGE);
+	LLVector4a fudge;
+	fudge.splat(SG_OCCLUSION_FUDGE);
 
-	for (U32 k = 0; k < 3; k++)
+	LLVector4a r;
+	r.setAdd(mBounds[1], fudge);
+
+	LLStrider<LLVector3> pos;
+	
 	{
-		r.mV[k] = llmin(mBounds[1].mV[k]+0.25f, r.mV[k]);
+		LLFastTimer t(FTM_BUILD_OCCLUSION);
+		mOcclusionVerts->getVertexStrider(pos);
 	}
 
-	F32* v = mOcclusionVerts;
-	F32* c = mBounds[0].mV;
-	F32* s = r.mV;
+	{
+		LLVector4a* v = (LLVector4a*) pos.get();
+
+		const LLVector4a& c = mBounds[0];
+		const LLVector4a& s = r;
+		
+		static const LLVector4a octant[] =
+		{
+			LLVector4a(-1.f, -1.f, -1.f),
+			LLVector4a(-1.f, -1.f, 1.f),
+			LLVector4a(-1.f, 1.f, -1.f),
+			LLVector4a(-1.f, 1.f, 1.f),
+
+			LLVector4a(1.f, -1.f, -1.f),
+			LLVector4a(1.f, -1.f, 1.f),
+			LLVector4a(1.f, 1.f, -1.f),
+			LLVector4a(1.f, 1.f, 1.f),
+		};
+
+		//vertex positions are encoded so the 3 bits of their vertex index 
+		//correspond to their axis facing, with bit position 3,2,1 matching
+		//axis facing x,y,z, bit set meaning positive facing, bit clear 
+		//meaning negative facing
+		
+		for (S32 i = 0; i < 8; ++i)
+		{
+			LLVector4a p;
+			p.setMul(s, octant[i]);
+			p.add(c);
+			v[i] = p;
+		}
+	}
 	
-	//vertex positions are encoded so the 3 bits of their vertex index 
-	//correspond to their axis facing, with bit position 3,2,1 matching
-	//axis facing x,y,z, bit set meaning positive facing, bit clear 
-	//meaning negative facing
-	v[0] = c[0]-s[0]; v[1]  = c[1]-s[1]; v[2]  = c[2]-s[2];  // 0 - 0000 
-	v[3] = c[0]-s[0]; v[4]  = c[1]-s[1]; v[5]  = c[2]+s[2];  // 1 - 0001
-	v[6] = c[0]-s[0]; v[7]  = c[1]+s[1]; v[8]  = c[2]-s[2];  // 2 - 0010
-	v[9] = c[0]-s[0]; v[10] = c[1]+s[1]; v[11] = c[2]+s[2];  // 3 - 0011
-																					   
-	v[12] = c[0]+s[0]; v[13] = c[1]-s[1]; v[14] = c[2]-s[2]; // 4 - 0100
-	v[15] = c[0]+s[0]; v[16] = c[1]-s[1]; v[17] = c[2]+s[2]; // 5 - 0101
-	v[18] = c[0]+s[0]; v[19] = c[1]+s[1]; v[20] = c[2]-s[2]; // 6 - 0110
-	v[21] = c[0]+s[0]; v[22] = c[1]+s[1]; v[23] = c[2]+s[2]; // 7 - 0111
+	{
+		mOcclusionVerts->setBuffer(0);
+	}
 
 	clearState(LLSpatialGroup::OCCLUSION_DIRTY);
 }
@@ -281,6 +363,11 @@ LLSpatialGroup::~LLSpatialGroup()
 		llerrs << "Illegal deletion of LLSpatialGroup!" << llendl;
 	}*/
 
+	if (gDebugGL)
+	{
+		gPipeline.checkReferences(this);
+	}
+
 	if (isState(DEAD))
 	{
 		sZombieGroups--;
@@ -288,12 +375,17 @@ LLSpatialGroup::~LLSpatialGroup()
 	
 	sNodeCount--;
 
-	if (gGLManager.mHasOcclusionQuery && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+	if (gGLManager.mHasOcclusionQuery)
 	{
-		sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+		for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
+		{
+			if (mOcclusionQuery[i])
+			{
+				sQueryPool.release(mOcclusionQuery[i]);
+			}
+		}
 	}
 
-	delete [] mOcclusionVerts;
 	mOcclusionVerts = NULL;
 
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
@@ -435,7 +527,7 @@ BOOL LLSpatialGroup::isRecentlyVisible() const
 
 BOOL LLSpatialGroup::isVisible() const
 {
-	return mVisible[LLViewerCamera::sCurCameraID] == LLDrawable::getCurrentFrame() ? TRUE : FALSE;
+	return mVisible[LLViewerCamera::sCurCameraID] >= LLDrawable::getCurrentFrame() ? TRUE : FALSE;
 }
 
 void LLSpatialGroup::setVisible()
@@ -450,8 +542,10 @@ void LLSpatialGroup::validate()
 	sg_assert(!isState(DIRTY));
 	sg_assert(!isDead());
 
-	LLVector3 myMin = mBounds[0] - mBounds[1];
-	LLVector3 myMax = mBounds[0] + mBounds[1];
+	LLVector4a myMin;
+	myMin.setSub(mBounds[0], mBounds[1]);
+	LLVector4a myMax;
+	myMax.setAdd(mBounds[0], mBounds[1]);
 
 	validateDrawMap();
 
@@ -483,16 +577,18 @@ void LLSpatialGroup::validate()
 		group->validate();
 		
 		//ensure all children are enclosed in this node
-		LLVector3 center = group->mBounds[0];
-		LLVector3 size = group->mBounds[1];
+		LLVector4a center = group->mBounds[0];
+		LLVector4a size = group->mBounds[1];
 		
-		LLVector3 min = center - size;
-		LLVector3 max = center + size;
+		LLVector4a min;
+		min.setSub(center, size);
+		LLVector4a max;
+		max.setAdd(center, size);
 		
 		for (U32 j = 0; j < 3; j++)
 		{
-			sg_assert(min.mV[j] >= myMin.mV[j]-0.02f);
-			sg_assert(max.mV[j] <= myMax.mV[j]+0.02f);
+			sg_assert(min[j] >= myMin[j]-0.02f);
+			sg_assert(max[j] <= myMax[j]+0.02f);
 		}
 	}
 
@@ -502,52 +598,8 @@ void LLSpatialGroup::validate()
 void LLSpatialGroup::checkStates()
 {
 #if LL_OCTREE_PARANOIA_CHECK
-	LLOctreeStateCheck checker;
-	checker.traverse(mOctreeNode);
-#endif
-}
-
-void validate_draw_info(LLDrawInfo& params)
-{
-#if LL_OCTREE_PARANOIA_CHECK
-	if (params.mVertexBuffer.isNull())
-	{
-		llerrs << "Draw batch has no vertex buffer." << llendl;
-	}
-	
-	//bad range
-	if (params.mStart >= params.mEnd)
-	{
-		llerrs << "Draw batch has invalid range." << llendl;
-	}
-	
-	if (params.mEnd >= (U32) params.mVertexBuffer->getNumVerts())
-	{
-		llerrs << "Draw batch has buffer overrun error." << llendl;
-	}
-	
-	if (params.mOffset + params.mCount > (U32) params.mVertexBuffer->getNumIndices())
-	{
-		llerrs << "Draw batch has index buffer ovverrun error." << llendl;
-	}
-	
-	//bad indices
-	U16* indicesp = (U16*) params.mVertexBuffer->getIndicesPointer();
-	if (indicesp)
-	{
-		for (U32 i = params.mOffset; i < params.mOffset+params.mCount; i++)
-		{
-			if (indicesp[i] < (U16)params.mStart)
-			{
-				llerrs << "Draw batch has vertex buffer index out of range error (index too low)." << llendl;
-			}
-			
-			if (indicesp[i] > (U16)params.mEnd)
-			{
-				llerrs << "Draw batch has vertex buffer index out of range error (index too high)." << llendl;
-			}
-		}
-	}
+	//LLOctreeStateCheck checker;
+	//checker.traverse(mOctreeNode);
 #endif
 }
 
@@ -560,8 +612,8 @@ void LLSpatialGroup::validateDrawMap()
 		for (drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
 		{
 			LLDrawInfo& params = **j;
-			
-			validate_draw_info(params);
+		
+			params.validate();
 		}
 	}
 #endif
@@ -572,19 +624,17 @@ BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 		
 	drawablep->updateSpatialExtents();
-	validate_drawable(drawablep);
 
 	OctreeNode* parent = mOctreeNode->getOctParent();
 	
 	if (mOctreeNode->isInside(drawablep->getPositionGroup()) && 
 		(mOctreeNode->contains(drawablep) ||
-		 (drawablep->getBinRadius() > mOctreeNode->getSize().mdV[0] &&
+		 (drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
 				parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY)))
 	{
 		unbound();
 		setState(OBJECT_DIRTY);
 		//setState(GEOM_DIRTY);
-		validate_drawable(drawablep);
 		return TRUE;
 	}
 		
@@ -602,7 +652,6 @@ BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_oc
 	else
 	{
 		drawablep->setSpatialGroup(this);
-		validate_drawable(drawablep);
 		setState(OBJECT_DIRTY | GEOM_DIRTY);
 		setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
 		gPipeline.markRebuild(this, TRUE);
@@ -703,7 +752,7 @@ void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
 
 }
 
-BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxOut)
+BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut)
 {	
 	const OctreeNode* node = mOctreeNode;
 
@@ -716,8 +765,8 @@ BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxO
 		return FALSE;
 	}
 
-	LLVector3& newMin = mObjectExtents[0];
-	LLVector3& newMax = mObjectExtents[1];
+	LLVector4a& newMin = mObjectExtents[0];
+	LLVector4a& newMax = mObjectExtents[1];
 	
 	if (isState(OBJECT_DIRTY))
 	{ //calculate new bounding box
@@ -726,10 +775,10 @@ BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxO
 		//initialize bounding box to first element
 		OctreeNode::const_element_iter i = node->getData().begin();
 		LLDrawable* drawablep = *i;
-		const LLVector3* minMax = drawablep->getSpatialExtents();
+		const LLVector4a* minMax = drawablep->getSpatialExtents();
 
-		newMin.setVec(minMax[0]);
-		newMax.setVec(minMax[1]);
+		newMin = minMax[0];
+		newMax = minMax[1];
 
 		for (++i; i != node->getData().end(); ++i)
 		{
@@ -753,8 +802,10 @@ BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxO
 			}*/
 		}
 		
-		mObjectBounds[0] = (newMin + newMax) * 0.5f;
-		mObjectBounds[1] = (newMax - newMin) * 0.5f;
+		mObjectBounds[0].setAdd(newMin, newMax);
+		mObjectBounds[0].mul(0.5f);
+		mObjectBounds[1].setSub(newMax, newMin);
+		mObjectBounds[1].mul(0.5f);
 	}
 	
 	if (empty)
@@ -764,17 +815,8 @@ BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxO
 	}
 	else
 	{
-		for (U32 i = 0; i < 3; i++)
-		{
-			if (newMin.mV[i] < minOut.mV[i])
-			{
-				minOut.mV[i] = newMin.mV[i];
-			}
-			if (newMax.mV[i] > maxOut.mV[i])
-			{
-				maxOut.mV[i] = newMax.mV[i];
-			}
-		}
+		minOut.setMin(minOut, newMin);
+		maxOut.setMax(maxOut, newMax);
 	}
 		
 	return TRUE;
@@ -865,18 +907,19 @@ BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
 	return TRUE;
 }
 
-void LLSpatialGroup::shift(const LLVector3 &offset)
+void LLSpatialGroup::shift(const LLVector4a &offset)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-	LLVector3d offsetd(offset);
-	mOctreeNode->setCenter(mOctreeNode->getCenter()+offsetd);
+	LLVector4a t = mOctreeNode->getCenter();
+	t.add(offset);	
+	mOctreeNode->setCenter(t);
 	mOctreeNode->updateMinMax();
-	mBounds[0] += offset;
-	mExtents[0] += offset;
-	mExtents[1] += offset;
-	mObjectBounds[0] += offset;
-	mObjectExtents[0] += offset;
-	mObjectExtents[1] += offset;
+	mBounds[0].add(offset);
+	mExtents[0].add(offset);
+	mExtents[1].add(offset);
+	mObjectBounds[0].add(offset);
+	mObjectExtents[0].add(offset);
+	mObjectExtents[1].add(offset);
 
 	//if (!mSpatialPartition->mRenderByGroup)
 	{
@@ -884,15 +927,9 @@ void LLSpatialGroup::shift(const LLVector3 &offset)
 		gPipeline.markRebuild(this, TRUE);
 	}
 
-	if (mOcclusionVerts)
+	if (mOcclusionVerts.notNull())
 	{
-		for (U32 i = 0; i < 8; i++)
-		{
-			F32* v = mOcclusionVerts+i*3;
-			v[0] += offset.mV[0];
-			v[1] += offset.mV[1];
-			v[2] += offset.mV[2];
-		}
+		setState(OCCLUSION_DIRTY);
 	}
 }
 
@@ -1079,12 +1116,23 @@ void LLSpatialGroup::setOcclusionState(U32 state, S32 mode)
 			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
 			{
 				mOcclusionState[i] |= state;
+
+				if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
+				{
+					sQueryPool.release(mOcclusionQuery[i]);
+					mOcclusionQuery[i] = 0;
+				}
 			}
 		}
 	}
 	else
 	{
 		mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
+		if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+		{
+			sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+			mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
+		}
 	}
 }
 
@@ -1152,13 +1200,11 @@ LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) :
 	mOctreeNode(node),
 	mSpatialPartition(part),
 	mVertexBuffer(NULL), 
-	mBufferUsage(GL_STATIC_DRAW_ARB),
+	mBufferUsage(part->mBufferUsage),
 	mDistance(0.f),
 	mDepth(0.f),
 	mLastUpdateDistance(-1.f), 
 	mLastUpdateTime(gFrameTimeSeconds),
-	mViewAngle(0.f),
-	mLastUpdateViewAngle(-1.f),
 	mAtlasList(4),
 	mCurUpdatingTime(0),
 	mCurUpdatingSlotp(NULL),
@@ -1167,13 +1213,18 @@ LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) :
 	sNodeCount++;
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 
+	mViewAngle.splat(0.f);
+	mLastUpdateViewAngle.splat(-1.f);
+	mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[0] = mObjectBounds[1] = 
+		mObjectExtents[0] = mObjectExtents[1] = mViewAngle;
+
 	sg_assert(mOctreeNode->getListenerCount() == 0);
 	mOctreeNode->addListener(this);
 	setState(SG_INITIAL_STATE_MASK);
 	gPipeline.markRebuild(this, TRUE);
 
-	mBounds[0] = LLVector3(node->getCenter());
-	mBounds[1] = LLVector3(node->getSize());
+	mBounds[0] = node->getCenter();
+	mBounds[1] = node->getSize();
 
 	part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
 	mLODHash = part->mLODSeed;
@@ -1210,8 +1261,8 @@ void LLSpatialGroup::updateDistance(LLCamera &camera)
 #endif
 	if (!getData().empty())
 	{
-		mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].magVec() :
-						(F32) mOctreeNode->getSize().magVec();
+		mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
+						(F32) mOctreeNode->getSize().getLength3().getF32();
 		mDistance = mSpatialPartition->calcDistance(this, camera);
 		mPixelArea = mSpatialPartition->calcPixelArea(this, camera);
 	}
@@ -1219,24 +1270,31 @@ void LLSpatialGroup::updateDistance(LLCamera &camera)
 
 F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
 {
-	LLVector3 eye = group->mObjectBounds[0] - camera.getOrigin();
+	LLVector4a eye;
+	LLVector4a origin;
+	origin.load3(camera.getOrigin().mV);
+
+	eye.setSub(group->mObjectBounds[0], origin);
 
 	F32 dist = 0.f;
 
 	if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
 	{
-		LLVector3 v = eye;
-		dist = eye.normVec();
+		LLVector4a v = eye;
+
+		dist = eye.getLength3().getF32();
+		eye.normalize3fast();
 
 		if (!group->isState(LLSpatialGroup::ALPHA_DIRTY))
 		{
 			if (!group->mSpatialPartition->isBridge())
 			{
-				LLVector3 view_angle = LLVector3(eye * LLVector3(1,0,0),
-												 eye * LLVector3(0,1,0),
-												 eye * LLVector3(0,0,1));
+				LLVector4a view_angle = eye;
 
-				if ((view_angle-group->mLastUpdateViewAngle).magVec() > 0.64f)
+				LLVector4a diff;
+				diff.setSub(view_angle, group->mLastUpdateViewAngle);
+
+				if (diff.getLength3().getF32() > 0.64f)
 				{
 					group->mViewAngle = view_angle;
 					group->mLastUpdateViewAngle = view_angle;
@@ -1253,17 +1311,20 @@ F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
 
 		LLVector3 at = camera.getAtAxis();
 
-		//front of bounding box
-		for (U32 i = 0; i < 3; i++)
-		{
-			v.mV[i] -= group->mObjectBounds[1].mV[i]*0.25f * at.mV[i];
-		}
+		LLVector4a ata;
+		ata.load3(at.mV);
 
-		group->mDepth = v * at;
+		LLVector4a t = ata;
+		//front of bounding box
+		t.mul(0.25f);
+		t.mul(group->mObjectBounds[1]);
+		v.sub(t);
+		
+		group->mDepth = v.dot3(ata).getF32();
 	}
 	else
 	{
-		dist = eye.magVec();
+		dist = eye.getLength3().getF32();
 	}
 
 	if (dist < 16.f)
@@ -1289,7 +1350,8 @@ F32 LLSpatialGroup::getUpdateUrgency() const
 	}
 	else
 	{
-		return (gFrameTimeSeconds - mLastUpdateTime+4.f)/mDistance;
+		F32 time = gFrameTimeSeconds-mLastUpdateTime+4.f;
+		return time + (mObjectBounds[1].dot3(mObjectBounds[1]).getF32()+1.f)/mDistance;
 	}
 }
 
@@ -1300,8 +1362,8 @@ BOOL LLSpatialGroup::needsUpdate()
 
 BOOL LLSpatialGroup::changeLOD()
 {
-	if (isState(ALPHA_DIRTY))
-	{ ///an alpha sort is going to happen, update distance and LOD
+	if (isState(ALPHA_DIRTY | OBJECT_DIRTY))
+	{ ///a rebuild is going to happen, update distance and LoD
 		return TRUE;
 	}
 
@@ -1314,7 +1376,7 @@ BOOL LLSpatialGroup::changeLOD()
 			return TRUE;
 		}
 
-		if (mDistance > mRadius)
+		if (mDistance > mRadius*2.f)
 		{
 			return FALSE;
 		}
@@ -1416,7 +1478,6 @@ void LLSpatialGroup::destroyGL()
 		}
 	}
 
-	delete [] mOcclusionVerts;
 	mOcclusionVerts = NULL;
 
 	for (LLSpatialGroup::element_iter i = getData().begin(); i != getData().end(); ++i)
@@ -1425,8 +1486,7 @@ void LLSpatialGroup::destroyGL()
 		for (S32 j = 0; j < drawable->getNumFaces(); j++)
 		{
 			LLFace* facep = drawable->getFace(j);
-			facep->mVertexBuffer = NULL;
-			facep->mLastVertexBuffer = NULL;
+			facep->clearVertexBuffer();
 		}
 	}
 }
@@ -1459,8 +1519,8 @@ BOOL LLSpatialGroup::rebound()
 	}
 	else
 	{
-		LLVector3& newMin = mExtents[0];
-		LLVector3& newMax = mExtents[1];
+		LLVector4a& newMin = mExtents[0];
+		LLVector4a& newMax = mExtents[1];
 		LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
 		group->clearState(SKIP_FRUSTUM_CHECK);
 		group->rebound();
@@ -1474,26 +1534,19 @@ BOOL LLSpatialGroup::rebound()
 			group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
 			group->clearState(SKIP_FRUSTUM_CHECK);
 			group->rebound();
-			const LLVector3& max = group->mExtents[1];
-			const LLVector3& min = group->mExtents[0];
+			const LLVector4a& max = group->mExtents[1];
+			const LLVector4a& min = group->mExtents[0];
 
-			for (U32 j = 0; j < 3; j++)
-			{
-				if (max.mV[j] > newMax.mV[j])
-				{
-					newMax.mV[j] = max.mV[j];
-				}
-				if (min.mV[j] < newMin.mV[j])
-				{
-					newMin.mV[j] = min.mV[j];
-				}
-			}
+			newMax.setMax(newMax, max);
+			newMin.setMin(newMin, min);
 		}
 
 		boundObjects(FALSE, newMin, newMax);
 		
-		mBounds[0] = (newMin + newMax)*0.5f;
-		mBounds[1] = (newMax - newMin)*0.5f;
+		mBounds[0].setAdd(newMin, newMax);
+		mBounds[0].mul(0.5f);
+		mBounds[1].setSub(newMax, newMin);
+		mBounds[1].mul(0.5f);
 	}
 	
 	setState(OCCLUSION_DIRTY);
@@ -1516,31 +1569,53 @@ void LLSpatialGroup::checkOcclusion()
 		}
 		else if (isOcclusionState(QUERY_PENDING))
 		{	//otherwise, if a query is pending, read it back
-			GLuint res = 1;
-			if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
-			{
-				glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);	
-			}
-
-			if (isOcclusionState(DISCARD_QUERY))
-			{
-				res = 2;
-			}
 
-			if (res > 0)
+			GLuint available = 0;
+			if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
 			{
-				assert_states_valid(this);
-				clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
-				assert_states_valid(this);
+				glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
 			}
 			else
 			{
-				assert_states_valid(this);
-				setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
-				assert_states_valid(this);
+				available = 1;
 			}
 
-			clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
+			if (available)
+			{ //result is available, read it back, otherwise wait until next frame
+				GLuint res = 1;
+				if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+				{
+					glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);	
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+					sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+#endif
+				}
+				else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
+				{ //delete the query to avoid holding onto hundreds of pending queries
+					sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+					mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
+				}
+				
+				if (isOcclusionState(DISCARD_QUERY))
+				{
+					res = 2;
+				}
+
+				if (res > 0)
+				{
+					assert_states_valid(this);
+					clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+					assert_states_valid(this);
+				}
+				else
+				{
+					assert_states_valid(this);
+					setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+					assert_states_valid(this);
+				}
+
+				clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
+			}
 		}
 		else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLSpatialGroup::OCCLUDED))
 		{	//check occlusion has been issued for occluded node that has not had a query issued
@@ -1551,14 +1626,18 @@ void LLSpatialGroup::checkOcclusion()
 	}
 }
 
+static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
+static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
+
 void LLSpatialGroup::doOcclusion(LLCamera* camera)
 {
 	if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
 	{
 		// Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
-		if ((mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER && !gGLManager.mHasDepthClamp) ||
-			earlyFail(camera, this))
+		if (earlyFail(camera, this))
 		{
+			LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
 			setOcclusionState(LLSpatialGroup::DISCARD_QUERY);
 			assert_states_valid(this);
 			clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
@@ -1566,54 +1645,82 @@ void LLSpatialGroup::doOcclusion(LLCamera* camera)
 		}
 		else
 		{
+			if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
 			{
-				LLFastTimer t(FTM_RENDER_OCCLUSION);
+				{ //no query pending, or previous query to be discarded
+					LLFastTimer t(FTM_RENDER_OCCLUSION);
 
-				if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
-				{
-					mOcclusionQuery[LLViewerCamera::sCurCameraID] = sQueryPool.allocate();
-				}
+					if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
+					{
+						mOcclusionQuery[LLViewerCamera::sCurCameraID] = sQueryPool.allocate();
+					}
 
-				if (!mOcclusionVerts || isState(LLSpatialGroup::OCCLUSION_DIRTY))
-				{
-					buildOcclusion();
-				}
-				
-				// Depth clamp all water to avoid it being culled as a result of being
-				// behind the far clip plane, and in the case of edge water to avoid
-				// it being culled while still visible.
-				bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
-											(mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||
-											mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
-				if (use_depth_clamp)
-				{
-					glEnable(GL_DEPTH_CLAMP);
-				}
-				
-				glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mOcclusionQuery[LLViewerCamera::sCurCameraID]);					
-				glVertexPointer(3, GL_FLOAT, 0, mOcclusionVerts);
-				if (camera->getOrigin().isExactlyZero())
-				{ //origin is invalid, draw entire box
-					glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8,
-												GL_UNSIGNED_BYTE, sOcclusionIndices);
-					glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8,
-							GL_UNSIGNED_BYTE, sOcclusionIndices+b111*8);
-				}
-				else
-				{
-					glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8,
-								GL_UNSIGNED_BYTE, get_box_fan_indices(camera, mBounds[0]));
+					if (mOcclusionVerts.isNull() || isState(LLSpatialGroup::OCCLUSION_DIRTY))
+					{
+						buildOcclusion();
+					}
+					
+					// Depth clamp all water to avoid it being culled as a result of being
+					// behind the far clip plane, and in the case of edge water to avoid
+					// it being culled while still visible.
+					bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
+												(mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||						
+												mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
+
+					LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);				
+						
+#if !LL_DARWIN					
+					U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
+#else
+					U32 mode = GL_SAMPLES_PASSED_ARB;
+#endif
+					
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+					sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+#endif
+
+					{
+						LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
+						glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);					
+					
+						mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
+
+						if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
+						{
+							LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
+							if (camera->getOrigin().isExactlyZero())
+							{ //origin is invalid, draw entire box
+								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
+								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
+							}
+							else
+							{
+								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
+							}
+						}
+						else
+						{
+							if (camera->getOrigin().isExactlyZero())
+							{ //origin is invalid, draw entire box
+								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
+								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
+							}
+							else
+							{
+								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
+							}
+						}
+
+						glEndQueryARB(mode);
+					}
 				}
-				glEndQueryARB(GL_SAMPLES_PASSED_ARB);
-				
-				if (use_depth_clamp)
+
 				{
-					glDisable(GL_DEPTH_CLAMP);
+					LLFastTimer t(FTM_SET_OCCLUSION_STATE);
+					setOcclusionState(LLSpatialGroup::QUERY_PENDING);
+					clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
 				}
 			}
-
-			setOcclusionState(LLSpatialGroup::QUERY_PENDING);
-			clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
 		}
 	}
 }
@@ -1635,8 +1742,11 @@ LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32
 	mSlopRatio = 0.25f;
 	mInfiniteFarClip = FALSE;
 
-	mOctree = new LLSpatialGroup::OctreeRoot(LLVector3d(0,0,0), 
-											LLVector3d(1,1,1), 
+	LLVector4a center, size;
+	center.splat(0.f);
+	size.splat(1.f);
+
+	mOctree = new LLSpatialGroup::OctreeRoot(center,size,
 											NULL);
 	new LLSpatialGroup(mOctree, this);
 }
@@ -1656,7 +1766,6 @@ LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 		
 	drawablep->updateSpatialExtents();
-	validate_drawable(drawablep);
 
 	//keep drawable from being garbage collected
 	LLPointer<LLDrawable> ptr = drawablep;
@@ -1740,16 +1849,16 @@ void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL
 class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
 {
 public:
-	LLSpatialShift(LLVector3 offset) : mOffset(offset) { }
+	const LLVector4a& mOffset;
+
+	LLSpatialShift(const LLVector4a& offset) : mOffset(offset) { }
 	virtual void visit(const LLSpatialGroup::OctreeNode* branch) 
 	{ 
 		((LLSpatialGroup*) branch->getListener(0))->shift(mOffset); 
 	}
-		
-	LLVector3 mOffset;
 };
 
-void LLSpatialPartition::shift(const LLVector3 &offset)
+void LLSpatialPartition::shift(const LLVector4a &offset)
 { //shift octree node bounding boxes by offset
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 	LLSpatialShift shifter(offset);
@@ -1911,7 +2020,7 @@ class LLOctreeCullShadow : public LLOctreeCull
 class LLOctreeCullVisExtents: public LLOctreeCullShadow
 {
 public:
-	LLOctreeCullVisExtents(LLCamera* camera, LLVector3& min, LLVector3& max)
+	LLOctreeCullVisExtents(LLCamera* camera, LLVector4a& min, LLVector4a& max)
 		: LLOctreeCullShadow(camera), mMin(min), mMax(max), mEmpty(TRUE) { }
 
 	virtual bool earlyFail(LLSpatialGroup* group)
@@ -1978,8 +2087,8 @@ class LLOctreeCullVisExtents: public LLOctreeCullShadow
 	}
 
 	BOOL mEmpty;
-	LLVector3& mMin;
-	LLVector3& mMax;
+	LLVector4a& mMin;
+	LLVector4a& mMax;
 };
 
 class LLOctreeCullDetectVisible: public LLOctreeCullShadow
@@ -2048,6 +2157,8 @@ class LLOctreeSelect : public LLOctreeCull
 
 void drawBox(const LLVector3& c, const LLVector3& r)
 {
+	LLVertexBuffer::unbind();
+
 	gGL.begin(LLRender::TRIANGLE_STRIP);
 	//left front
 	gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
@@ -2083,6 +2194,11 @@ void drawBox(const LLVector3& c, const LLVector3& r)
 	gGL.end();	
 }
 
+void drawBox(const LLVector4a& c, const LLVector4a& r)
+{
+	drawBox(reinterpret_cast<const LLVector3&>(c), reinterpret_cast<const LLVector3&>(r));
+}
+
 void drawBoxOutline(const LLVector3& pos, const LLVector3& size)
 {
 	LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
@@ -2129,6 +2245,11 @@ void drawBoxOutline(const LLVector3& pos, const LLVector3& size)
 	gGL.end();
 }
 
+void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size)
+{
+	drawBoxOutline(reinterpret_cast<const LLVector3&>(pos), reinterpret_cast<const LLVector3&>(size));
+}
+
 class LLOctreeDirty : public LLOctreeTraveler<LLDrawable>
 {
 public:
@@ -2172,14 +2293,21 @@ BOOL LLSpatialPartition::isOcclusionEnabled()
 
 BOOL LLSpatialPartition::getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax)
 {
+	LLVector4a visMina, visMaxa;
+	visMina.load3(visMin.mV);
+	visMaxa.load3(visMax.mV);
+
 	{
 		LLFastTimer ftm(FTM_CULL_REBOUND);		
 		LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
 		group->rebound();
 	}
 
-	LLOctreeCullVisExtents vis(&camera, visMin, visMax);
+	LLOctreeCullVisExtents vis(&camera, visMina, visMaxa);
 	vis.traverse(mOctree);
+
+	visMin.set(visMina.getF32ptr());
+	visMax.set(visMaxa.getF32ptr());
 	return vis.mEmpty;
 }
 
@@ -2242,25 +2370,36 @@ BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
 	}
 
 	const F32 vel = SG_OCCLUSION_FUDGE*2.f;
-	LLVector3 c = group->mBounds[0];
-	LLVector3 r = group->mBounds[1] + LLVector3(vel,vel,vel);
-    
+	LLVector4a fudge;
+	fudge.splat(vel);
+
+	const LLVector4a& c = group->mBounds[0];
+	LLVector4a r;
+	r.setAdd(group->mBounds[1], fudge);
+
 	/*if (r.magVecSquared() > 1024.0*1024.0)
 	{
 		return TRUE;
 	}*/
 
-	LLVector3 e = camera->getOrigin();
+	LLVector4a e;
+	e.load3(camera->getOrigin().mV);
 	
-	LLVector3 min = c - r;
-	LLVector3 max = c + r;
+	LLVector4a min;
+	min.setSub(c,r);
+	LLVector4a max;
+	max.setAdd(c,r);
 	
-	for (U32 j = 0; j < 3; j++)
+	S32 lt = e.lessThan(min).getGatheredBits() & 0x7;
+	if (lt)
 	{
-		if (e.mV[j] < min.mV[j] || e.mV[j] > max.mV[j])
-		{
-			return FALSE;
-		}
+		return FALSE;
+	}
+
+	S32 gt = e.greaterThan(max).getGatheredBits() & 0x7;
+	if (gt)
+	{
+		return FALSE;
 	}
 
 	return TRUE;
@@ -2291,7 +2430,9 @@ void pushVerts(LLSpatialGroup* group, U32 mask)
 
 void pushVerts(LLFace* face, U32 mask)
 {
-	LLVertexBuffer* buffer = face->mVertexBuffer;
+	llassert(face->verify());
+
+	LLVertexBuffer* buffer = face->getVertexBuffer();
 
 	if (buffer)
 	{
@@ -2302,7 +2443,25 @@ void pushVerts(LLFace* face, U32 mask)
 		U16 offset = face->getIndicesStart();
 		buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
 	}
+}
+
+void pushVerts(LLDrawable* drawable, U32 mask)
+{
+	for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+	{
+		pushVerts(drawable->getFace(i), mask);
+	}
+}
 
+void pushVerts(LLVolume* volume)
+{
+	LLVertexBuffer::unbind();
+	for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+	{
+		const LLVolumeFace& face = volume->getVolumeFace(i);
+		glVertexPointer(3, GL_FLOAT, 16, face.mPositions);
+		glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
+	}
 }
 
 void pushBufferVerts(LLVertexBuffer* buffer, U32 mask)
@@ -2380,7 +2539,6 @@ void renderOctree(LLSpatialGroup* group)
 {
 	//render solid object bounding box, color
 	//coded by buffer usage and activity
-	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
 	gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
 	LLVector4 col;
 	if (group->mBuilt > 0.f)
@@ -2421,7 +2579,7 @@ void renderOctree(LLSpatialGroup* group)
 				for (S32 j = 0; j < drawable->getNumFaces(); j++)
 				{
 					LLFace* face = drawable->getFace(j);
-					if (face->mVertexBuffer.notNull())
+					if (face->getVertexBuffer())
 					{
 						if (gFrameTimeSeconds - face->mLastUpdateTime < 0.5f)
 						{
@@ -2436,10 +2594,10 @@ void renderOctree(LLSpatialGroup* group)
 							continue;
 						}
 
-						face->mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX);
+						face->getVertexBuffer()->setBuffer(LLVertexBuffer::MAP_VERTEX);
 						//drawBox((face->mExtents[0] + face->mExtents[1])*0.5f,
 						//		(face->mExtents[1]-face->mExtents[0])*0.5f);
-						face->mVertexBuffer->draw(LLRender::TRIANGLES, face->getIndicesCount(), face->getIndicesStart());
+						face->getVertexBuffer()->draw(LLRender::TRIANGLES, face->getIndicesCount(), face->getIndicesStart());
 					}
 				}
 
@@ -2466,7 +2624,16 @@ void renderOctree(LLSpatialGroup* group)
 	}
 
 	gGL.color4fv(col.mV);
-	drawBox(group->mObjectBounds[0], group->mObjectBounds[1]*1.01f+LLVector3(0.001f, 0.001f, 0.001f));
+	LLVector4a fudge;
+	fudge.splat(0.001f);
+	LLVector4a size = group->mObjectBounds[1];
+	size.mul(1.01f);
+	size.add(fudge);
+
+	{
+		LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+		drawBox(group->mObjectBounds[0], fudge);
+	}
 	
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 
@@ -2497,8 +2664,12 @@ void renderOctree(LLSpatialGroup* group)
 				for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
 				{
 					LLDrawInfo* draw_info = *j;
-					LLVector3 center = (draw_info->mExtents[1] + draw_info->mExtents[0])*0.5f;
-					LLVector3 size = (draw_info->mExtents[1] - draw_info->mExtents[0])*0.5f;
+					LLVector4a center;
+					center.setAdd(draw_info->mExtents[1], draw_info->mExtents[0]);
+					center.mul(0.5f);
+					LLVector4a size;
+					size.setSub(draw_info->mExtents[1], draw_info->mExtents[0]);
+					size.mul(0.5f);
 					drawBoxOutline(center, size);
 				}
 			}
@@ -2545,17 +2716,17 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
 			gGL.color4f(0.f, 0.75f, 0.f, 0.5f);
 			pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
 		}
-		else if (camera && group->mOcclusionVerts)
+		else if (camera && group->mOcclusionVerts.notNull())
 		{
 			LLVertexBuffer::unbind();
-			glVertexPointer(3, GL_FLOAT, 0, group->mOcclusionVerts);
-
+			group->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
+			
 			glColor4f(1.0f, 0.f, 0.f, 0.5f);
-			glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, GL_UNSIGNED_BYTE, get_box_fan_indices(camera, group->mBounds[0]));
+			group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
 			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 			
 			glColor4f(1.0f, 1.f, 1.f, 1.0f);
-			glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, GL_UNSIGNED_BYTE, get_box_fan_indices(camera, group->mBounds[0]));
+			group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
 			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 		}
 	}
@@ -2671,8 +2842,8 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
 		}
 	}
 
-	const LLVector3* ext;
-	LLVector3 pos, size;
+	const LLVector4a* ext;
+	LLVector4a pos, size;
 
 	//render face bounding boxes
 	for (S32 i = 0; i < drawable->getNumFaces(); i++)
@@ -2681,20 +2852,21 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
 
 		ext = facep->mExtents;
 
-		if (ext[0].isExactlyZero() && ext[1].isExactlyZero())
-		{
-			continue;
-		}
-		pos = (ext[0] + ext[1]) * 0.5f;
-		size = (ext[1] - ext[0]) * 0.5f;
+		pos.setAdd(ext[0], ext[1]);
+		pos.mul(0.5f);
+		size.setSub(ext[1], ext[0]);
+		size.mul(0.5f);
+		
 		drawBoxOutline(pos,size);
 	}
 
 	//render drawable bounding box
 	ext = drawable->getSpatialExtents();
 
-	pos = (ext[0] + ext[1]) * 0.5f;
-	size = (ext[1] - ext[0]) * 0.5f;
+	pos.setAdd(ext[0], ext[1]);
+	pos.mul(0.5f);
+	size.setSub(ext[1], ext[0]);
+	size.mul(0.5f);
 	
 	LLViewerObject* vobj = drawable->getVObj();
 	if (vobj && vobj->onActiveList())
@@ -2711,17 +2883,516 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
 	{
 		drawBoxOutline(pos,size);
 	}
-	
 }
 
-void renderTexturePriority(LLDrawable* drawable)
+void renderNormals(LLDrawable* drawablep)
 {
-	for (int face=0; face<drawable->getNumFaces(); ++face)
+	LLVertexBuffer::unbind();
+
+	LLVOVolume* vol = drawablep->getVOVolume();
+	if (vol)
 	{
-		LLFace *facep = drawable->getFace(face);
+		LLVolume* volume = vol->getVolume();
+		gGL.pushMatrix();
+		glMultMatrixf((F32*) vol->getRelativeXform().mMatrix);
 		
-		LLVector4 cold(0,0,0.25f);
-		LLVector4 hot(1,0.25f,0.25f);
+		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+		LLVector4a scale(gSavedSettings.getF32("RenderDebugNormalScale"));
+
+		for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+		{
+			const LLVolumeFace& face = volume->getVolumeFace(i);
+
+			gGL.begin(LLRender::LINES);
+			
+			for (S32 j = 0; j < face.mNumVertices; ++j)
+			{
+				LLVector4a n,p;
+				
+				n.setMul(face.mNormals[j], scale);
+				p.setAdd(face.mPositions[j], n);
+				
+				gGL.color4f(1,1,1,1);
+				gGL.vertex3fv(face.mPositions[j].getF32ptr());
+				gGL.vertex3fv(p.getF32ptr());
+				
+				if (face.mBinormals)
+				{
+					n.setMul(face.mBinormals[j], scale);
+					p.setAdd(face.mPositions[j], n);
+				
+					gGL.color4f(0,1,1,1);
+					gGL.vertex3fv(face.mPositions[j].getF32ptr());
+					gGL.vertex3fv(p.getF32ptr());
+				}	
+			}
+
+			gGL.end();
+		}
+
+		gGL.popMatrix();
+	}
+}
+
+S32 get_physics_detail(const LLVolumeParams& volume_params, const LLVector3& scale)
+{
+	const S32 DEFAULT_DETAIL = 1;
+	const F32 LARGE_THRESHOLD = 5.f;
+	const F32 MEGA_THRESHOLD = 25.f;
+
+	S32 detail = DEFAULT_DETAIL;
+	F32 avg_scale = (scale[0]+scale[1]+scale[2])/3.f;
+
+	if (avg_scale > LARGE_THRESHOLD)
+	{
+		detail += 1;
+		if (avg_scale > MEGA_THRESHOLD)
+		{
+			detail += 1;
+		}
+	}
+
+	return detail;
+}
+
+void renderMeshBaseHull(LLVOVolume* volume, U32 data_mask, LLColor4& color, LLColor4& line_color)
+{
+	LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
+	LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
+
+	const LLVector3 center(0,0,0);
+	const LLVector3 size(0.25f,0.25f,0.25f);
+
+	if (decomp)
+	{		
+		if (!decomp->mBaseHullMesh.empty())
+		{
+			glColor4fv(color.mV);
+			LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mBaseHullMesh.mPositions, decomp->mBaseHullMesh.mNormals);
+		}
+		else
+		{
+			gMeshRepo.buildPhysicsMesh(*decomp);
+			gGL.color3f(0,1,1);
+			drawBoxOutline(center, size);
+		}
+
+	}
+	else
+	{
+		gGL.color3f(1,0,1);
+		drawBoxOutline(center, size);
+	}
+}
+
+void render_hull(LLModel::PhysicsMesh& mesh, const LLColor4& color, const LLColor4& line_color)
+{
+	glColor4fv(color.mV);
+	LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
+	LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
+	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+	glPolygonOffset(3.f, 3.f);
+	glLineWidth(3.f);
+	glColor4fv(line_color.mV);
+	LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
+	glLineWidth(1.f);
+	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
+
+void renderPhysicsShape(LLDrawable* drawable, LLVOVolume* volume)
+{
+	if (volume->isSelected())
+	{
+		LLVector3 construct_me(5,5,5);
+		construct_me.normalize();
+	}
+
+	
+	U8 physics_type = volume->getPhysicsShapeType();
+
+	if (physics_type == LLViewerObject::PHYSICS_SHAPE_NONE || volume->isFlexible())
+	{
+		return;
+	}
+
+	//not allowed to return at this point without rendering *something*
+
+	F32 threshold = gSavedSettings.getF32("ObjectCostHighThreshold");
+	F32 cost = volume->getObjectCost();
+
+	LLColor4 low = gSavedSettings.getColor4("ObjectCostLowColor");
+	LLColor4 mid = gSavedSettings.getColor4("ObjectCostMidColor");
+	LLColor4 high = gSavedSettings.getColor4("ObjectCostHighColor");
+
+	F32 normalizedCost = 1.f - exp( -(cost / threshold) );
+
+	LLColor4 color;
+	if ( normalizedCost <= 0.5f )
+	{
+		color = lerp( low, mid, 2.f * normalizedCost );
+	}
+	else
+	{
+		color = lerp( mid, high, 2.f * ( normalizedCost - 0.5f ) );
+	}
+
+	LLColor4 line_color = color*0.5f;
+
+	U32 data_mask = LLVertexBuffer::MAP_VERTEX;
+
+	LLVolumeParams volume_params = volume->getVolume()->getParams();
+
+	LLPhysicsVolumeParams physics_params(volume_params, 
+		physics_type == LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); 
+
+	LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification physics_spec;
+	LLPhysicsShapeBuilderUtil::determinePhysicsShape(physics_params, volume->getScale(), physics_spec);
+
+	U32 type = physics_spec.getType();
+
+	LLVector3 center(0,0,0);
+	LLVector3 size(0.25f,0.25f,0.25f);
+
+	gGL.pushMatrix();
+	glMultMatrixf((F32*) volume->getRelativeXform().mMatrix);
+		
+	if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_MESH)
+	{
+		LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
+		LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
+			
+		if (decomp)
+		{ //render a physics based mesh
+			
+			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+			if (!decomp->mHull.empty())
+			{ //decomposition exists, use that
+
+				if (decomp->mMesh.empty())
+				{
+					gMeshRepo.buildPhysicsMesh(*decomp);
+				}
+
+				for (U32 i = 0; i < decomp->mMesh.size(); ++i)
+				{		
+					render_hull(decomp->mMesh[i], color, line_color);
+				}
+			}
+			else if (!decomp->mPhysicsShapeMesh.empty())
+			{ 
+				//decomp has physics mesh, render that mesh
+				glColor4fv(color.mV);
+				LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
+								
+				glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+				glColor4fv(line_color.mV);
+				LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
+				glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+			}
+			else
+			{ //no mesh or decomposition, render base hull
+				renderMeshBaseHull(volume, data_mask, color, line_color);
+
+				if (decomp->mPhysicsShapeMesh.empty())
+				{
+					//attempt to fetch physics shape mesh if available
+					gMeshRepo.fetchPhysicsShape(mesh_id);
+				}
+			}
+		}
+		else
+		{	
+			gGL.color3f(1,1,0);
+			drawBoxOutline(center, size);
+		}
+	}
+	else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_CONVEX ||
+		type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
+	{
+		if (volume->isMesh())
+		{
+			renderMeshBaseHull(volume, data_mask, color, line_color);
+		}
+#if LL_WINDOWS 
+		else
+		{
+			LLVolumeParams volume_params = volume->getVolume()->getParams();
+			S32 detail = get_physics_detail(volume_params, volume->getScale());
+			LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
+
+			if (!phys_volume->mHullPoints)
+			{ //build convex hull
+				std::vector<LLVector3> pos;
+				std::vector<U16> index;
+
+				S32 index_offset = 0;
+
+				for (S32 i = 0; i < phys_volume->getNumVolumeFaces(); ++i)
+				{
+					const LLVolumeFace& face = phys_volume->getVolumeFace(i);
+					if (index_offset + face.mNumVertices > 65535)
+					{
+						continue;
+					}
+
+					for (S32 j = 0; j < face.mNumVertices; ++j)
+					{
+						pos.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+					}
+
+					for (S32 j = 0; j < face.mNumIndices; ++j)
+					{
+						index.push_back(face.mIndices[j]+index_offset);
+					}
+
+					index_offset += face.mNumVertices;
+				}
+
+				if (!pos.empty() && !index.empty())
+				{
+					LLCDMeshData mesh;
+					mesh.mIndexBase = &index[0];
+					mesh.mVertexBase = pos[0].mV;
+					mesh.mNumVertices = pos.size();
+					mesh.mVertexStrideBytes = 12;
+					mesh.mIndexStrideBytes = 6;
+					mesh.mIndexType = LLCDMeshData::INT_16;
+
+					mesh.mNumTriangles = index.size()/3;
+					
+					LLCDMeshData res;
+
+					LLConvexDecomposition::getInstance()->generateSingleHullMeshFromMesh( &mesh, &res );
+
+					//copy res into phys_volume
+					phys_volume->mHullPoints = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*res.mNumVertices);
+					phys_volume->mNumHullPoints = res.mNumVertices;
+
+					S32 idx_size = (res.mNumTriangles*3*2+0xF) & ~0xF;
+					phys_volume->mHullIndices = (U16*) ll_aligned_malloc_16(idx_size);
+					phys_volume->mNumHullIndices = res.mNumTriangles*3;
+
+					const F32* v = res.mVertexBase;
+
+					for (S32 i = 0; i < res.mNumVertices; ++i)
+					{
+						F32* p = (F32*) ((U8*)v+i*res.mVertexStrideBytes);
+						phys_volume->mHullPoints[i].load3(p);
+					}
+
+					if (res.mIndexType == LLCDMeshData::INT_16)
+					{
+						for (S32 i = 0; i < res.mNumTriangles; ++i)
+						{
+							U16* idx = (U16*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
+
+							phys_volume->mHullIndices[i*3+0] = idx[0];
+							phys_volume->mHullIndices[i*3+1] = idx[1];
+							phys_volume->mHullIndices[i*3+2] = idx[2];
+						}
+					}
+					else
+					{
+						for (S32 i = 0; i < res.mNumTriangles; ++i)
+						{
+							U32* idx = (U32*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
+
+							phys_volume->mHullIndices[i*3+0] = (U16) idx[0];
+							phys_volume->mHullIndices[i*3+1] = (U16) idx[1];
+							phys_volume->mHullIndices[i*3+2] = (U16) idx[2];
+						}
+					}
+				}
+			}
+
+			if (phys_volume->mHullPoints)
+			{
+				//render hull
+			
+				glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+				
+				glColor4fv(line_color.mV);
+				LLVertexBuffer::unbind();
+
+				glVertexPointer(3, GL_FLOAT, 16, phys_volume->mHullPoints);
+				glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
+				
+				glColor4fv(color.mV);
+				glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+				glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
+			}
+			else
+			{
+				gGL.color3f(1,0,1);
+				drawBoxOutline(center, size);
+			}
+
+			LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
+		}
+#endif //LL_WINDOWS			
+	}
+	else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::BOX)
+	{
+		LLVector3 center = physics_spec.getCenter();
+		LLVector3 scale = physics_spec.getScale();
+		LLVector3 vscale = volume->getScale()*2.f;
+		scale.set(scale[0]/vscale[0], scale[1]/vscale[1], scale[2]/vscale[2]);
+		
+		gGL.color4fv(color.mV);
+		drawBox(center, scale);
+	}
+	else if	(type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SPHERE)
+	{
+		LLVolumeParams volume_params;
+		volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
+		volume_params.setBeginAndEndS( 0.f, 1.f );
+		volume_params.setBeginAndEndT( 0.f, 1.f );
+		volume_params.setRatio	( 1, 1 );
+		volume_params.setShear	( 0, 0 );
+		LLVolume* sphere = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
+		
+		glColor4fv(color.mV);
+		pushVerts(sphere);
+		LLPrimitive::sVolumeManager->unrefVolume(sphere);
+	}
+	else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::CYLINDER)
+	{
+		LLVolumeParams volume_params;
+		volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
+		volume_params.setBeginAndEndS( 0.f, 1.f );
+		volume_params.setBeginAndEndT( 0.f, 1.f );
+		volume_params.setRatio	( 1, 1 );
+		volume_params.setShear	( 0, 0 );
+		LLVolume* cylinder = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
+		
+		glColor4fv(color.mV);
+		pushVerts(cylinder);
+		LLPrimitive::sVolumeManager->unrefVolume(cylinder);
+	}
+	else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_MESH)
+	{
+		LLVolumeParams volume_params = volume->getVolume()->getParams();
+		S32 detail = get_physics_detail(volume_params, volume->getScale());
+
+		LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
+		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+		
+		glColor4fv(line_color.mV);
+		pushVerts(phys_volume);
+		
+		glColor4fv(color.mV);
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+		pushVerts(phys_volume);
+		LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
+	}
+	else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
+	{
+		LLVolumeParams volume_params = volume->getVolume()->getParams();
+		S32 detail = get_physics_detail(volume_params, volume->getScale());
+
+		LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
+
+		if (phys_volume->mHullPoints && phys_volume->mHullIndices)
+		{
+			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+			
+			LLVertexBuffer::unbind();
+			glVertexPointer(3, GL_FLOAT, 16, phys_volume->mHullPoints);
+			glColor4fv(line_color.mV);
+			glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
+			
+			glColor4fv(color.mV);
+			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+			glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);			
+		}
+		else
+		{
+			gGL.color3f(1,0,1);
+			drawBoxOutline(center, size);
+			gMeshRepo.buildHull(volume_params, detail);
+		}
+		LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
+	}
+	else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SCULPT)
+	{
+		//TODO: implement sculpted prim physics display
+	}
+	else 
+	{
+		llerrs << "Unhandled type" << llendl;
+	}
+
+	gGL.popMatrix();
+
+	/*{ //analytical shape, just push visual rep.
+		glColor3fv(color.mV);
+		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+		pushVerts(drawable, data_mask);
+		glColor4fv(color.mV);
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+		pushVerts(drawable, data_mask);
+	}*/
+}
+
+void renderPhysicsShapes(LLSpatialGroup* group)
+{
+	for (LLSpatialGroup::OctreeNode::const_element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+	{
+		LLDrawable* drawable = *i;
+		LLVOVolume* volume = drawable->getVOVolume();
+		if (volume && !volume->isAttachment() && volume->getPhysicsShapeType() != LLViewerObject::PHYSICS_SHAPE_NONE )
+		{
+			if (!group->mSpatialPartition->isBridge())
+			{
+				gGL.pushMatrix();
+				LLVector3 trans = drawable->getRegion()->getOriginAgent();
+				glTranslatef(trans.mV[0], trans.mV[1], trans.mV[2]);
+				renderPhysicsShape(drawable, volume);
+				gGL.popMatrix();
+			}
+			else
+			{
+				renderPhysicsShape(drawable, volume);
+			}
+		}
+		else
+		{
+			LLViewerObject* object = drawable->getVObj();
+			if (object && object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+			{
+				//push face vertices for terrain
+				for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+				{
+					LLFace* face = drawable->getFace(i);
+					LLVertexBuffer* buff = face->getVertexBuffer();
+					if (buff)
+					{
+						glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+						buff->setBuffer(LLVertexBuffer::MAP_VERTEX);
+						glColor3f(0.2f, 0.5f, 0.3f);
+						buff->draw(LLRender::TRIANGLES, buff->getRequestedIndices(), 0);
+									
+						glColor3f(0.2f, 1.f, 0.3f);
+						glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+						buff->draw(LLRender::TRIANGLES, buff->getRequestedIndices(), 0);
+					}
+				}
+			}
+		}
+	}
+}
+
+void renderTexturePriority(LLDrawable* drawable)
+{
+	for (int face=0; face<drawable->getNumFaces(); ++face)
+	{
+		LLFace *facep = drawable->getFace(face);
+		
+		LLVector4 cold(0,0,0.25f);
+		LLVector4 hot(1,0.25f,0.25f);
 	
 		LLVector4 boost_cold(0,0,0,0);
 		LLVector4 boost_hot(0,1,0,1);
@@ -2750,8 +3421,13 @@ void renderTexturePriority(LLDrawable* drawable)
 		//	gGL.color4f(1,0,1,1);
 		//}
 		
-		LLVector3 center = (facep->mExtents[1]+facep->mExtents[0])*0.5f;
-		LLVector3 size = (facep->mExtents[1]-facep->mExtents[0])*0.5f + LLVector3(0.01f, 0.01f, 0.01f);
+		LLVector4a center;
+		center.setAdd(facep->mExtents[1],facep->mExtents[0]);
+		center.mul(0.5f);
+		LLVector4a size;
+		size.setSub(facep->mExtents[1],facep->mExtents[0]);
+		size.mul(0.5f);
+		size.add(LLVector4a(0.01f));
 		drawBox(center, size);
 		
 		/*S32 boost = imagep->getBoostLevel();
@@ -2775,7 +3451,6 @@ void renderPoints(LLDrawable* drawablep)
 	{
 		gGL.begin(LLRender::POINTS);
 		gGL.color3f(1,1,1);
-		LLVector3 center(drawablep->getPositionGroup());
 		for (S32 i = 0; i < drawablep->getNumFaces(); i++)
 		{
 			gGL.vertex3fv(drawablep->getFace(i)->mCenterLocal.mV);
@@ -2807,8 +3482,12 @@ void renderShadowFrusta(LLDrawInfo* params)
 	LLGLEnable blend(GL_BLEND);
 	gGL.setSceneBlendType(LLRender::BT_ADD);
 
-	LLVector3 center = (params->mExtents[1]+params->mExtents[0])*0.5f;
-	LLVector3 size = (params->mExtents[1]-params->mExtents[0])*0.5f;
+	LLVector4a center;
+	center.setAdd(params->mExtents[1], params->mExtents[0]);
+	center.mul(0.5f);
+	LLVector4a size;
+	size.setSub(params->mExtents[1],params->mExtents[0]);
+	size.mul(0.5f);
 
 	if (gPipeline.mShadowCamera[4].AABBInFrustum(center, size))
 	{
@@ -2852,10 +3531,14 @@ void renderLights(LLDrawable* drawablep)
 			pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
 		}
 
-		const LLVector3* ext = drawablep->getSpatialExtents();
+		const LLVector4a* ext = drawablep->getSpatialExtents();
 
-		LLVector3 pos = (ext[0] + ext[1]) * 0.5f;
-		LLVector3 size = (ext[1] - ext[0]) * 0.5f;
+		LLVector4a pos;
+		pos.setAdd(ext[0], ext[1]);
+		pos.mul(0.5f);
+		LLVector4a size;
+		size.setSub(ext[1], ext[0]);
+		size.mul(0.5f);
 
 		{
 			LLGLDepthTest depth(GL_FALSE, GL_TRUE);
@@ -2865,65 +3548,206 @@ void renderLights(LLDrawable* drawablep)
 
 		gGL.color4f(1,1,0,1);
 		F32 rad = drawablep->getVOVolume()->getLightRadius();
-		drawBoxOutline(pos, LLVector3(rad,rad,rad));
+		drawBoxOutline(pos, LLVector4a(rad));
 	}
 }
 
-
-void renderRaycast(LLDrawable* drawablep)
+class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
 {
-	if (drawablep->getVObj() != gDebugRaycastObject)
+public:
+	
+	
+	LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t)
+		: LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL)
 	{
-		return;
+
 	}
-	
+
+	void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+	{
+		LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) branch->getListener(0);
+
+		LLVector3 center, size;
+		
+		if (branch->getData().empty())
+		{
+			gGL.color3f(1.f,0.2f,0.f);
+			center.set(branch->getCenter().getF32ptr());
+			size.set(branch->getSize().getF32ptr());
+		}
+		else
+		{
+			gGL.color3f(0.75f, 1.f, 0.f);
+			center.set(vl->mBounds[0].getF32ptr());
+			size.set(vl->mBounds[1].getF32ptr());
+		}
+
+		drawBoxOutline(center, size);	
+		
+		for (U32 i = 0; i < 2; i++)
+		{
+			LLGLDepthTest depth(GL_TRUE, GL_FALSE, i == 1 ? GL_LEQUAL : GL_GREATER);
+
+			if (i == 1)
+			{
+				gGL.color4f(0,1,1,0.5f);
+			}
+			else
+			{
+				gGL.color4f(0,0.5f,0.5f, 0.25f);
+				drawBoxOutline(center, size);
+			}
+			
+			if (i == 1)
+			{
+				gGL.flush();
+				glLineWidth(3.f);
+			}
+
+			gGL.begin(LLRender::TRIANGLES);
+			for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getData().begin();
+					iter != branch->getData().end();
+					++iter)
+			{
+				const LLVolumeTriangle* tri = *iter;
+				
+				gGL.vertex3fv(tri->mV[0]->getF32ptr());
+				gGL.vertex3fv(tri->mV[1]->getF32ptr());
+				gGL.vertex3fv(tri->mV[2]->getF32ptr());
+			}	
+			gGL.end();
+
+			if (i == 1)
+			{
+				gGL.flush();
+				glLineWidth(1.f);
+			}
+		}
+	}
+};
+
+void renderRaycast(LLDrawable* drawablep)
+{
 	if (drawablep->getNumFaces())
 	{
 		LLGLEnable blend(GL_BLEND);
 		gGL.color4f(0,1,1,0.5f);
 
-		if (drawablep->getVOVolume() && gDebugRaycastFaceHit != -1)
+		if (drawablep->getVOVolume())
 		{
-			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-			pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX);
-			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+			//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+			//pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX);
+			//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+			LLVOVolume* vobj = drawablep->getVOVolume();
+			LLVolume* volume = vobj->getVolume();
+
+			bool transform = true;
+			if (drawablep->isState(LLDrawable::RIGGED))
+			{
+				volume = vobj->getRiggedVolume();
+				transform = false;
+			}
+
+			if (volume)
+			{
+				LLVector3 trans = drawablep->getRegion()->getOriginAgent();
+				
+				for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+				{
+					const LLVolumeFace& face = volume->getVolumeFace(i);
+					if (!face.mOctree)
+					{
+						((LLVolumeFace*) &face)->createOctree(); 
+					}
+
+					gGL.pushMatrix();
+					glTranslatef(trans.mV[0], trans.mV[1], trans.mV[2]);					
+					glMultMatrixf((F32*) vobj->getRelativeXform().mMatrix);
+
+					LLVector3 start, end;
+					if (transform)
+					{
+						start = vobj->agentPositionToVolume(gDebugRaycastStart);
+						end = vobj->agentPositionToVolume(gDebugRaycastEnd);
+					}
+					else
+					{
+						start = gDebugRaycastStart;
+						end = gDebugRaycastEnd;
+					}
+
+					LLVector4a starta, enda;
+					starta.load3(start.mV);
+					enda.load3(end.mV);
+					LLVector4a dir;
+					dir.setSub(enda, starta);
+
+					F32 t = 1.f;
+
+					LLRenderOctreeRaycast render(starta, dir, &t);
+					gGL.flush();
+					glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);				
+
+					{
+						//render face positions
+						LLVertexBuffer::unbind();
+						glColor4f(0,1,1,0.5f);
+						glVertexPointer(3, GL_FLOAT, sizeof(LLVector4a), face.mPositions);
+						glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
+					}
+						
+					render.traverse(face.mOctree);
+					gGL.popMatrix();		
+					glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+				}
+			}
 		}
 		else if (drawablep->isAvatar())
 		{
-			LLGLDepthTest depth(GL_FALSE);
-			LLVOAvatar* av = (LLVOAvatar*) drawablep->getVObj().get();
-			av->renderCollisionVolumes();
-		}
-
-		// draw intersection point
-		glPushMatrix();
-		glLoadMatrixd(gGLModelView);
-		LLVector3 translate = gDebugRaycastIntersection;
-		glTranslatef(translate.mV[0], translate.mV[1], translate.mV[2]);
-		LLCoordFrame orient;
-		orient.lookDir(gDebugRaycastNormal, gDebugRaycastBinormal);
-		LLMatrix4 rotation;
-		orient.getRotMatrixToParent(rotation);
-		glMultMatrixf((float*)rotation.mMatrix);
-		
-		gGL.color4f(1,0,0,0.5f);
-		drawBox(LLVector3(0, 0, 0), LLVector3(0.1f, 0.022f, 0.022f));
-		gGL.color4f(0,1,0,0.5f);
-		drawBox(LLVector3(0, 0, 0), LLVector3(0.021f, 0.1f, 0.021f));
-		gGL.color4f(0,0,1,0.5f);
-		drawBox(LLVector3(0, 0, 0), LLVector3(0.02f, 0.02f, 0.1f));
-		glPopMatrix();
-
-		// draw bounding box of prim
-		const LLVector3* ext = drawablep->getSpatialExtents();
-
-		LLVector3 pos = (ext[0] + ext[1]) * 0.5f;
-		LLVector3 size = (ext[1] - ext[0]) * 0.5f;
+			if (drawablep->getVObj() == gDebugRaycastObject)
+			{
+				LLGLDepthTest depth(GL_FALSE);
+				LLVOAvatar* av = (LLVOAvatar*) drawablep->getVObj().get();
+				av->renderCollisionVolumes();
+			}
+		}
 
-		LLGLDepthTest depth(GL_FALSE, GL_TRUE);
-		gGL.color4f(0,0.5f,0.5f,1);
-		drawBoxOutline(pos, size);
+		if (drawablep->getVObj() == gDebugRaycastObject)
+		{
+			// draw intersection point
+			glPushMatrix();
+			glLoadMatrixd(gGLModelView);
+			LLVector3 translate = gDebugRaycastIntersection;
+			glTranslatef(translate.mV[0], translate.mV[1], translate.mV[2]);
+			LLCoordFrame orient;
+			orient.lookDir(gDebugRaycastNormal, gDebugRaycastBinormal);
+			LLMatrix4 rotation;
+			orient.getRotMatrixToParent(rotation);
+			glMultMatrixf((float*)rotation.mMatrix);
+			
+			gGL.color4f(1,0,0,0.5f);
+			drawBox(LLVector3(0, 0, 0), LLVector3(0.1f, 0.022f, 0.022f));
+			gGL.color4f(0,1,0,0.5f);
+			drawBox(LLVector3(0, 0, 0), LLVector3(0.021f, 0.1f, 0.021f));
+			gGL.color4f(0,0,1,0.5f);
+			drawBox(LLVector3(0, 0, 0), LLVector3(0.02f, 0.02f, 0.1f));
+			glPopMatrix();
+
+			// draw bounding box of prim
+			const LLVector4a* ext = drawablep->getSpatialExtents();
+
+			LLVector4a pos;
+			pos.setAdd(ext[0], ext[1]);
+			pos.mul(0.5f);
+			LLVector4a size;
+			size.setSub(ext[1], ext[0]);
+			size.mul(0.5f);
 
+			LLGLDepthTest depth(GL_FALSE, GL_TRUE);
+			gGL.color4f(0,0.5f,0.5f,1);
+			drawBoxOutline(pos, size);		
+		}
 	}
 }
 
@@ -2945,7 +3769,6 @@ void renderAgentTarget(LLVOAvatar* avatar)
 	}
 }
 
-
 class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 {
 public:
@@ -3005,8 +3828,8 @@ class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 			return;
 		}
 
-		LLVector3 nodeCenter = group->mBounds[0];
-		LLVector3 octCenter = LLVector3(group->mOctreeNode->getCenter());
+		LLVector4a nodeCenter = group->mBounds[0];
+		LLVector4a octCenter = group->mOctreeNode->getCenter();
 
 		group->rebuildGeom();
 		group->rebuildMesh();
@@ -3029,14 +3852,25 @@ class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 			{
 				renderBoundingBox(drawable);			
 			}
+
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_NORMALS))
+			{
+				renderNormals(drawable);
+			}
 			
 			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BUILD_QUEUE))
 			{
 				if (drawable->isState(LLDrawable::IN_REBUILD_Q2))
 				{
 					gGL.color4f(0.6f, 0.6f, 0.1f, 1.f);
-					const LLVector3* ext = drawable->getSpatialExtents();
-					drawBoxOutline((ext[0]+ext[1])*0.5f, (ext[1]-ext[0])*0.5f);
+					const LLVector4a* ext = drawable->getSpatialExtents();
+					LLVector4a center;
+					center.setAdd(ext[0], ext[1]);
+					center.mul(0.5f);
+					LLVector4a size;
+					size.setSub(ext[1], ext[0]);
+					size.mul(0.5f);
+					drawBoxOutline(center, size);
 				}
 			}	
 
@@ -3101,6 +3935,41 @@ class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 	}
 };
 
+
+class LLOctreeRenderPhysicsShapes : public LLOctreeTraveler<LLDrawable>
+{
+public:
+	LLCamera* mCamera;
+	LLOctreeRenderPhysicsShapes(LLCamera* camera): mCamera(camera) {}
+	
+	virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+	{
+		LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+		
+		if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]))
+		{
+			node->accept(this);
+			stop_glerror();
+
+			for (U32 i = 0; i < node->getChildCount(); i++)
+			{
+				traverse(node->getChild(i));
+				stop_glerror();
+			}
+			
+			group->rebuildGeom();
+			group->rebuildMesh();
+
+			renderPhysicsShapes(group);
+		}
+	}
+
+	virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+	{
+		
+	}
+};
+
 class LLOctreePushBBoxVerts : public LLOctreeTraveler<LLDrawable>
 {
 public:
@@ -3219,6 +4088,25 @@ class LLOctreeStateCheck : public LLOctreeTraveler<LLDrawable>
 };
 
 
+void LLSpatialPartition::renderPhysicsShapes()
+{
+	LLSpatialBridge* bridge = asBridge();
+	LLCamera* camera = LLViewerCamera::getInstance();
+	
+	if (bridge)
+	{
+		camera = NULL;
+	}
+
+	gGL.flush();
+	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+	glLineWidth(3.f);
+	LLOctreeRenderPhysicsShapes render_physics(camera);
+	render_physics.traverse(mOctree);
+	gGL.flush();
+	glLineWidth(1.f);
+}
+
 void LLSpatialPartition::renderDebug()
 {
 	if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE |
@@ -3227,13 +4115,14 @@ void LLSpatialPartition::renderDebug()
 									  LLPipeline::RENDER_DEBUG_BATCH_SIZE |
 									  LLPipeline::RENDER_DEBUG_UPDATE_TYPE |
 									  LLPipeline::RENDER_DEBUG_BBOXES |
+									  LLPipeline::RENDER_DEBUG_NORMALS |
 									  LLPipeline::RENDER_DEBUG_POINTS |
 									  LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY |
 									  LLPipeline::RENDER_DEBUG_TEXTURE_ANIM |
 									  LLPipeline::RENDER_DEBUG_RAYCAST |
 									  LLPipeline::RENDER_DEBUG_AVATAR_VOLUME |
 									  LLPipeline::RENDER_DEBUG_AGENT_TARGET |
-									  LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
+									  //LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
 									  LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) 
 	{
 		return;
@@ -3272,7 +4161,11 @@ void LLSpatialPartition::renderDebug()
 void LLSpatialGroup::drawObjectBox(LLColor4 col)
 {
 	gGL.color4fv(col.mV);
-	drawBox(mObjectBounds[0], mObjectBounds[1]*1.01f+LLVector3(0.001f, 0.001f, 0.001f));
+	LLVector4a size;
+	size = mObjectBounds[1];
+	size.mul(1.01f);
+	size.add(LLVector4a(0.001f));
+	drawBox(mObjectBounds[0], size);
 }
 
 
@@ -3332,8 +4225,8 @@ class LLOctreeIntersect : public LLSpatialGroup::OctreeTraveler
 
 			LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
 			
-			LLVector3 size;
-			LLVector3 center;
+			LLVector4a size;
+			LLVector4a center;
 			
 			size = group->mBounds[1];
 			center = group->mBounds[0];
@@ -3350,7 +4243,11 @@ class LLOctreeIntersect : public LLSpatialGroup::OctreeTraveler
 				local_end   = mEnd   * local_matrix;
 			}
 
-			if (LLLineSegmentBoxIntersect(local_start, local_end, center, size))
+			LLVector4a start, end;
+			start.load3(local_start.mV);
+			end.load3(local_end.mV);
+
+			if (LLLineSegmentBoxIntersect(start, end, center, size))
 			{
 				check(child);
 			}
@@ -3440,18 +4337,9 @@ LLDrawInfo::LLDrawInfo(U16 start, U16 end, U32 count, U32 offset,
 	mDistance(0.f),
 	mDrawMode(LLRender::TRIANGLES)
 {
+	mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
+	
 	mDebugColor = (rand() << 16) + rand();
-	if (mStart >= mVertexBuffer->getRequestedVerts() ||
-		mEnd >= mVertexBuffer->getRequestedVerts())
-	{
-		llerrs << "Invalid draw info vertex range." << llendl;
-	}
-
-	if (mOffset >= (U32) mVertexBuffer->getRequestedIndices() ||
-		mOffset + mCount > (U32) mVertexBuffer->getRequestedIndices())
-	{
-		llerrs << "Invalid draw info index range." << llendl;
-	}
 }
 
 LLDrawInfo::~LLDrawInfo()	
@@ -3465,6 +4353,16 @@ LLDrawInfo::~LLDrawInfo()
 	{
 		mFace->setDrawInfo(NULL);
 	}
+
+	if (gDebugGL)
+	{
+		gPipeline.checkReferences(this);
+	}
+}
+
+void LLDrawInfo::validate()
+{
+	mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
 }
 
 LLVertexBuffer* LLGeometryManager::createVertexBuffer(U32 type_mask, U32 usage)
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 2b9cf6c6300498ea17b122b1e259b73ef3cf8aa6..0d9cad914a55755344cc062719da89ec364a05d7 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -39,7 +39,7 @@
 #include "lldrawpool.h"
 #include "llface.h"
 #include "llviewercamera.h"
-
+#include "llvector4a.h"
 #include <queue>
 
 #define SG_STATE_INHERIT_MASK (OCCLUDED)
@@ -51,11 +51,16 @@ class LLSpatialGroup;
 class LLTextureAtlas;
 class LLTextureAtlasSlot;
 
+S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad);
+S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &radius_squared);
+
 S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad);
 S32 AABBSphereIntersectR2(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &radius_squared);
+void pushVerts(LLFace* face, U32 mask);
 
 // get index buffer for binary encoded axis vertex buffer given a box at center being viewed by given camera
-U8* get_box_fan_indices(LLCamera* camera, const LLVector3& center);
+U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center);
+U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center);
 
 class LLDrawInfo : public LLRefCount 
 {
@@ -63,11 +68,27 @@ class LLDrawInfo : public LLRefCount
 	~LLDrawInfo();	
 	
 public:
+
+	LLDrawInfo(const LLDrawInfo& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLDrawInfo& operator=(const LLDrawInfo& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
 	LLDrawInfo(U16 start, U16 end, U32 count, U32 offset, 
 				LLViewerTexture* image, LLVertexBuffer* buffer, 
 				BOOL fullbright = FALSE, U8 bump = 0, BOOL particle = FALSE, F32 part_size = 0);
 	
 
+	void validate();
+
+	LLVector4a mExtents[2];
+	
 	LLPointer<LLVertexBuffer> mVertexBuffer;
 	LLPointer<LLViewerTexture>     mTexture;
 	LLColor4U mGlowColor;
@@ -86,7 +107,6 @@ class LLDrawInfo : public LLRefCount
 	LLSpatialGroup* mGroup;
 	LLFace* mFace; //associated face
 	F32 mDistance;
-	LLVector3 mExtents[2];
 	U32 mDrawMode;
 
 	struct CompareTexture
@@ -128,6 +148,17 @@ class LLDrawInfo : public LLRefCount
 
 	};
 
+	struct CompareMatrixTexturePtr
+	{
+		bool operator()(const LLPointer<LLDrawInfo>& lhs, const LLPointer<LLDrawInfo>& rhs)	
+		{
+			return lhs.get() != rhs.get() 
+				&& (lhs.isNull() || (rhs.notNull() && (lhs->mModelMatrix > rhs->mModelMatrix ||
+													   (lhs->mModelMatrix == rhs->mModelMatrix && lhs->mTexture.get() > rhs->mTexture.get()))));
+		}
+
+	};
+
 	struct CompareBump
 	{
 		bool operator()(const LLPointer<LLDrawInfo>& lhs, const LLPointer<LLDrawInfo>& rhs) 
@@ -149,11 +180,25 @@ class LLDrawInfo : public LLRefCount
 	};
 };
 
+LL_ALIGN_PREFIX(64)
 class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 {
 	friend class LLSpatialPartition;
 	friend class LLOctreeStateCheck;
 public:
+
+	LLSpatialGroup(const LLSpatialGroup& rhs)
+	{
+		*this = rhs;
+	}
+
+	const LLSpatialGroup& operator=(const LLSpatialGroup& rhs)
+	{
+		llerrs << "Illegal operation!" << llendl;
+		return *this;
+	}
+
+	static std::set<GLuint> sPendingQueries; //pending occlusion queries
 	static U32 sNodeCount;
 	static BOOL sNoDelete; //deletion of spatial groups and draw info not allowed if TRUE
 
@@ -262,8 +307,8 @@ class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 	BOOL isVisible() const;
 	BOOL isRecentlyVisible() const;
 	void setVisible();
-	void shift(const LLVector3 &offset);
-	BOOL boundObjects(BOOL empty, LLVector3& newMin, LLVector3& newMax);
+	void shift(const LLVector4a &offset);
+	BOOL boundObjects(BOOL empty, LLVector4a& newMin, LLVector4a& newMax);
 	void unbound();
 	BOOL rebound();
 	void buildOcclusion(); //rebuild mOcclusionVerts
@@ -311,6 +356,27 @@ class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 	void addAtlas(LLTextureAtlas* atlasp, S8 recursive_level = 3) ;
 	void removeAtlas(LLTextureAtlas* atlasp, BOOL remove_group = TRUE, S8 recursive_level = 3) ;
 	void clearAtlasList() ;
+
+public:
+
+	typedef enum
+	{
+		BOUNDS = 0,
+		EXTENTS = 2,
+		OBJECT_BOUNDS = 4,
+		OBJECT_EXTENTS = 6,
+		VIEW_ANGLE = 8,
+		LAST_VIEW_ANGLE = 9,
+		V4_COUNT = 10
+	} eV4Index;
+
+	LLVector4a mBounds[2]; // bounding box (center, size) of this node and all its children (tight fit to objects)
+	LLVector4a mExtents[2]; // extents (min, max) of this node and all its children
+	LLVector4a mObjectExtents[2]; // extents (min, max) of objects in this node
+	LLVector4a mObjectBounds[2]; // bounding box (center, size) of objects in this node
+	LLVector4a mViewAngle;
+	LLVector4a mLastUpdateViewAngle;
+		
 private:
 	U32                     mCurUpdatingTime ;
 	//do not make the below two to use LLPointer
@@ -338,14 +404,9 @@ class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 	F32 mBuilt;
 	OctreeNode* mOctreeNode;
 	LLSpatialPartition* mSpatialPartition;
-	LLVector3 mBounds[2]; // bounding box (center, size) of this node and all its children (tight fit to objects)
-	LLVector3 mExtents[2]; // extents (min, max) of this node and all its children
 	
-	LLVector3 mObjectExtents[2]; // extents (min, max) of objects in this node
-	LLVector3 mObjectBounds[2]; // bounding box (center, size) of objects in this node
-
 	LLPointer<LLVertexBuffer> mVertexBuffer;
-	F32*					mOcclusionVerts;
+	LLPointer<LLVertexBuffer> mOcclusionVerts;
 	GLuint					mOcclusionQuery[LLViewerCamera::NUM_CAMERAS];
 
 	U32 mBufferUsage;
@@ -356,13 +417,10 @@ class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 	F32 mDepth;
 	F32 mLastUpdateDistance;
 	F32 mLastUpdateTime;
-			
-	LLVector3 mViewAngle;
-	LLVector3 mLastUpdateViewAngle;
 	
 	F32 mPixelArea;
 	F32 mRadius;
-};
+} LL_ALIGN_POSTFIX(64);
 
 class LLGeometryManager
 {
@@ -398,7 +456,7 @@ class LLSpatialPartition: public LLGeometryManager
 	
 	// If the drawable moves, move it here.
 	virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
-	virtual void shift(const LLVector3 &offset);
+	virtual void shift(const LLVector4a &offset);
 
 	virtual F32 calcDistance(LLSpatialGroup* group, LLCamera& camera);
 	virtual F32 calcPixelArea(LLSpatialGroup* group, LLCamera& camera);
@@ -414,6 +472,7 @@ class LLSpatialPartition: public LLGeometryManager
 	virtual LLSpatialBridge* asBridge() { return NULL; }
 	virtual BOOL isBridge() { return asBridge() != NULL; }
 
+	void renderPhysicsShapes();
 	void renderDebug();
 	void renderIntersectingBBoxes(LLCamera* camera);
 	void restoreGL();
@@ -456,7 +515,7 @@ class LLSpatialBridge : public LLDrawable, public LLSpatialPartition
 	virtual void makeActive();
 	virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
 	virtual BOOL updateMove();
-	virtual void shiftPos(const LLVector3& vec);
+	virtual void shiftPos(const LLVector4a& vec);
 	virtual void cleanupReferences();
 	virtual LLSpatialPartition* asPartition()		{ return this; }
 	virtual LLSpatialBridge* asBridge()				{ return this; }
@@ -484,6 +543,7 @@ class LLCullResult
 	sg_list_t::iterator beginAlphaGroups();
 	sg_list_t::iterator endAlphaGroups();
 
+	bool hasOcclusionGroups() { return mOcclusionGroupsSize > 0; }
 	sg_list_t::iterator beginOcclusionGroups();
 	sg_list_t::iterator endOcclusionGroups();
 
@@ -613,6 +673,13 @@ class LLCloudPartition : public LLParticlePartition
 class LLVolumeGeometryManager: public LLGeometryManager
 {
  public:
+	typedef enum
+	{
+		NONE = 0,
+		BATCH_SORT,
+		DISTANCE_SORT
+	} eSortType;
+
 	virtual ~LLVolumeGeometryManager() { }
 	virtual void rebuildGeom(LLSpatialGroup* group);
 	virtual void rebuildMesh(LLSpatialGroup* group);
@@ -647,7 +714,7 @@ class LLHUDBridge : public LLVolumeBridge
 {
 public:
 	LLHUDBridge(LLDrawable* drawablep);
-	virtual void shiftPos(const LLVector3& vec);
+	virtual void shiftPos(const LLVector4a& vec);
 	virtual F32 calcPixelArea(LLSpatialGroup* group, LLCamera& camera);
 };
 
@@ -664,11 +731,9 @@ class LLHUDPartition : public LLBridgePartition
 {
 public:
 	LLHUDPartition();
-	virtual void shift(const LLVector3 &offset);
+	virtual void shift(const LLVector4a &offset);
 };
 
-void validate_draw_info(LLDrawInfo& params);
-
 extern const F32 SG_BOX_SIDE;
 extern const F32 SG_BOX_OFFSET;
 extern const F32 SG_BOX_RAD;
diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp
index a69592b61c4372e1a37e4a7f6f204370c86ffc6e..4bde2dfcab55160ec92b88633588ae6dec55c6f5 100644
--- a/indra/newview/llsprite.cpp
+++ b/indra/newview/llsprite.cpp
@@ -186,14 +186,15 @@ void LLSprite::updateFace(LLFace &face)
 	U16 index_offset;
 
 	// Setup face
-	if (face.mVertexBuffer.isNull())
+	if (!face.getVertexBuffer())
 	{	
-		face.mVertexBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | 
+		LLVertexBuffer* buff = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | 
 												LLVertexBuffer::MAP_TEXCOORD0,
 												GL_STREAM_DRAW_ARB);
-		face.mVertexBuffer->allocateBuffer(4, 12, TRUE);
+		buff->allocateBuffer(4, 12, TRUE);
 		face.setGeomIndex(0);
 		face.setIndicesIndex(0);
+		face.setVertexBuffer(buff);
 	}
 		
 	index_offset = face.getGeometry(verticesp,normalsp,tex_coordsp, indicesp);
@@ -242,7 +243,7 @@ void LLSprite::updateFace(LLFace &face)
 		*indicesp++ = 3 + index_offset;
 	}
 
-	face.mVertexBuffer->setBuffer(0);
+	face.getVertexBuffer()->setBuffer(0);
 	face.mCenterAgent = mPosition;
 }
 
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index ca908ef8222df6587c8cec7d38ff68ff638e9ee6..277d2430ce8dbb818387221f7c1ec130d4145901 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -421,7 +421,7 @@ bool idle_startup()
 		//
 
 		// Load autopilot and stats stuff
-		gAgentPilot.load(gSavedSettings.getString("StatsPilotFile"));
+		gAgentPilot.load();
 
 		//gErrorStream.setTime(gSavedSettings.getBOOL("LogTimestamps"));
 
@@ -1959,7 +1959,7 @@ bool idle_startup()
 		}
 		
 		// Start automatic replay if the flag is set.
-		if (gSavedSettings.getBOOL("StatsAutoRun") || LLAgentPilot::sReplaySession)
+		if (gSavedSettings.getBOOL("StatsAutoRun") || gAgentPilot.getReplaySession())
 		{
 			LLUUID id;
 			LL_DEBUGS("AppInit") << "Starting automatic playback" << LL_ENDL;
@@ -3070,6 +3070,11 @@ bool process_login_success_response()
 	}
 
 	// Request the map server url
+	// Non-agni grids have a different default location.
+	if (!LLGridManager::getInstance()->isInProductionGrid())
+	{
+		gSavedSettings.setString("MapServerURL", "http://test.map.secondlife.com.s3.amazonaws.com/");
+	}
 	std::string map_server_url = response["map-server-url"];
 	if(!map_server_url.empty())
 	{
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
index 1d57e27616edcc6499b3bdf6392b2d2eba842542..5077c2c7e1f4bbdbd72ea6f642fecb0681bb8f08 100644
--- a/indra/newview/llsurfacepatch.cpp
+++ b/indra/newview/llsurfacepatch.cpp
@@ -854,8 +854,10 @@ void LLSurfacePatch::updateVisibility()
 	F32 stride_per_distance = DEFAULT_DELTA_ANGLE / mSurfacep->getMetersPerGrid();
 	U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
 
-	LLVector3 center = mCenterRegion + mSurfacep->getOriginAgent();
-	LLVector3 radius = LLVector3(mRadius, mRadius, mRadius);
+	LLVector4a center;
+	center.load3( (mCenterRegion + mSurfacep->getOriginAgent()).mV);
+	LLVector4a radius;
+	radius.splat(mRadius);
 
 	// sphere in frustum on global coordinates
 	if (LLViewerCamera::getInstance()->AABBInFrustumNoFarClip(center, radius))
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 56e97393508d2c152f46bba7e22ef67274933716..1023a4339b2e06a0d301eb5c9c8c4cd36a0e1800 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -289,7 +289,9 @@ BOOL LLFloaterTexturePicker::handleDragAndDrop(
 {
 	BOOL handled = FALSE;
 
-	if (cargo_type == DAD_TEXTURE)
+	bool is_mesh = cargo_type == DAD_MESH;
+
+	if ((cargo_type == DAD_TEXTURE) || is_mesh)
 	{
 		LLInventoryItem *item = (LLInventoryItem *)cargo_data;
 
@@ -1206,7 +1208,11 @@ BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,
 	// returns true, then the cast was valid, and we can perform
 	// the third test without problems.
 	LLInventoryItem* item = (LLInventoryItem*)cargo_data; 
-	if (getEnabled() && (cargo_type == DAD_TEXTURE) && allowDrop(item))
+	bool is_mesh = cargo_type == DAD_MESH;
+
+	if (getEnabled() &&
+		((cargo_type == DAD_TEXTURE) || is_mesh) &&
+		 allowDrop(item))
 	{
 		if (drop)
 		{
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 671a3346008e090751b1dd07b3341939a5b31a4d..d23d2b3abdbab4f27a1b93b02bba4b228d7f66fc 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -307,23 +307,24 @@ LLToolDragAndDrop::dragOrDrop3dImpl LLToolDragAndDrop::LLDragAndDropDictionary::
 
 LLToolDragAndDrop::LLDragAndDropDictionary::LLDragAndDropDictionary()
 {
- 	//       										 DT_NONE        DT_SELF                     DT_AVATAR                   DT_OBJECT                       DT_LAND		
-	//      										|--------------|---------------------------|---------------------------|-------------------------------|--------------|
+ 	//       										 DT_NONE                         DT_SELF                                        DT_AVATAR                   					DT_OBJECT                       					DT_LAND		
+	//      										|-------------------------------|----------------------------------------------|-----------------------------------------------|---------------------------------------------------|--------------------------------|
 	addEntry(DAD_NONE, 			new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,						&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_TEXTURE, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,		&LLToolDragAndDrop::dad3dTextureObject,			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_SOUND, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,		&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_TEXTURE, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dTextureObject,				&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_SOUND, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
 	addEntry(DAD_CALLINGCARD, 	new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_LANDMARK, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, 	&LLToolDragAndDrop::dad3dNULL, 				&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_SCRIPT, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, 	&LLToolDragAndDrop::dad3dNULL, 				&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dRezScript, 				&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_CLOTHING, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, 	&LLToolDragAndDrop::dad3dWearItem, 			&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_OBJECT, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, 	&LLToolDragAndDrop::dad3dRezAttachmentFromInv,	&LLToolDragAndDrop::dad3dGiveInventoryObject,	&LLToolDragAndDrop::dad3dRezObjectOnObject, 		&LLToolDragAndDrop::dad3dRezObjectOnLand));
-	addEntry(DAD_NOTECARD, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, 	&LLToolDragAndDrop::dad3dNULL, 				&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_CATEGORY, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, 	&LLToolDragAndDrop::dad3dWearCategory,			&LLToolDragAndDrop::dad3dGiveInventoryCategory,&LLToolDragAndDrop::dad3dUpdateInventoryCategory,	&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_LANDMARK, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, 					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_SCRIPT, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, 					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dRezScript, 				&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_CLOTHING, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearItem, 				&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_OBJECT, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dRezAttachmentFromInv,	&LLToolDragAndDrop::dad3dGiveInventoryObject,	&LLToolDragAndDrop::dad3dRezObjectOnObject, 		&LLToolDragAndDrop::dad3dRezObjectOnLand));
+	addEntry(DAD_NOTECARD, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, 					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_CATEGORY, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearCategory,			&LLToolDragAndDrop::dad3dGiveInventoryCategory,	&LLToolDragAndDrop::dad3dUpdateInventoryCategory,	&LLToolDragAndDrop::dad3dNULL));
 	addEntry(DAD_ROOT_CATEGORY, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,						&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_BODYPART, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dWearItem,				&LLToolDragAndDrop::dad3dGiveInventory,		&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_ANIMATION, 	new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,		&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
-	addEntry(DAD_GESTURE, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dActivateGesture,		&LLToolDragAndDrop::dad3dGiveInventory,		&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_BODYPART, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dWearItem,				&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_ANIMATION, 	new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_GESTURE, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dActivateGesture,		&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
 	addEntry(DAD_LINK, 			new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,						&LLToolDragAndDrop::dad3dNULL));
+	addEntry(DAD_MESH, 			new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dMeshObject,				&LLToolDragAndDrop::dad3dNULL));
 	// TODO: animation on self could play it?  edit it?
 	// TODO: gesture on self could play it?  edit it?
 };
@@ -396,7 +397,7 @@ void LLToolDragAndDrop::beginDrag(EDragAndDropType type,
 			{
 				folder_ids.push_back(cargo_id);
 			}
-			gInventory.collectDescendentsIf (
+			gInventory.collectDescendentsIf(
 				cargo_id,
 				cats,
 				items,
@@ -467,7 +468,7 @@ void LLToolDragAndDrop::beginMultiDrag(
 				{
 					cat_ids.insert(cat->getUUID());
 				}
-				gInventory.collectDescendentsIf (
+				gInventory.collectDescendentsIf(
 					cat->getUUID(),
 					cats,
 					items,
@@ -1027,6 +1028,31 @@ void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj,
 	hit_obj->sendTEUpdate();
 }
 
+void LLToolDragAndDrop::dropMesh(LLViewerObject* hit_obj,
+								 LLInventoryItem* item,
+								 LLToolDragAndDrop::ESource source,
+								 const LLUUID& src_id)
+{
+	if (!item)
+	{
+		llwarns << "no inventory item." << llendl;
+		return;
+	}
+	LLUUID asset_id = item->getAssetUUID();
+	BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
+	if(!success)
+	{
+		return;
+	}
+
+	LLSculptParams sculpt_params;
+	sculpt_params.setSculptTexture(asset_id);
+	sculpt_params.setSculptType(LL_SCULPT_TYPE_MESH);
+	hit_obj->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE);
+	
+	dialog_refresh_all();
+}
+
 /*
 void LLToolDragAndDrop::dropTextureOneFaceAvatar(LLVOAvatar* avatar, S32 hit_face, LLInventoryItem* item)
 {
@@ -1123,9 +1149,9 @@ void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,
 }
 
 void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target,
-								   BOOL bypass_sim_raycast,
-								   BOOL from_task_inventory,
-								   BOOL remove_from_inventory)
+				   BOOL bypass_sim_raycast,
+				   BOOL from_task_inventory,
+				   BOOL remove_from_inventory)
 {
 	LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(mLastHitPos);
 	if (!regionp)
@@ -1361,7 +1387,7 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL
 	// help make sure that drops that are from an object to an object
 	// don't have to worry about order of evaluation. Think of this
 	// like check for self in assignment.
-	if (obj->getID() == item->getParentUUID())
+	if(obj->getID() == item->getParentUUID())
 	{
 		return ACCEPT_NO;
 	}
@@ -1370,17 +1396,19 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL
 	//							  gAgent.getGroupID())
 	//			 && (obj->mPermModify || obj->mFlagAllowInventoryAdd));
 	BOOL worn = FALSE;
+	LLVOAvatarSelf* my_avatar = NULL;
 	switch(item->getType())
 	{
 	case LLAssetType::AT_OBJECT:
-		if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getUUID()))
+		my_avatar = gAgentAvatarp;
+		if(my_avatar && my_avatar->isWearingAttachment(item->getUUID()))
 		{
 				worn = TRUE;
 		}
 		break;
 	case LLAssetType::AT_BODYPART:
 	case LLAssetType::AT_CLOTHING:
-		if (gAgentWearables.isWearingItem(item->getUUID()))
+		if(gAgentWearables.isWearingItem(item->getUUID()))
 		{
 			worn = TRUE;
 		}
@@ -1395,7 +1423,7 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL
 	const LLPermissions& perm = item->getPermissions();
 	BOOL modify = (obj->permModify() || obj->flagAllowInventoryAdd());
 	BOOL transfer = FALSE;
-	if ((obj->permYouOwner() && (perm.getOwner() == gAgent.getID()))
+	if((obj->permYouOwner() && (perm.getOwner() == gAgent.getID()))
 	   || perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
 	{
 		transfer = TRUE;
@@ -1403,15 +1431,15 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL
 	BOOL volume = (LL_PCODE_VOLUME == obj->getPCode());
 	BOOL attached = obj->isAttachment();
 	BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE;
-	if (attached && !unrestricted)
+	if(attached && !unrestricted)
 	{
 		return ACCEPT_NO_LOCKED;
 	}
-	else if (modify && transfer && volume && !worn)
+	else if(modify && transfer && volume && !worn)
 	{
 		return ACCEPT_YES_MULTI;
 	}
-	else if (!modify)
+	else if(!modify)
 	{
 		return ACCEPT_NO_LOCKED;
 	}
@@ -1506,14 +1534,15 @@ bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_
 	case DAD_ANIMATION:
 	case DAD_GESTURE:
 	case DAD_CALLINGCARD:
+	case DAD_MESH:
 	{
 		LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
-		if (gInventory.getItem(inv_item->getUUID())
+		if(gInventory.getItem(inv_item->getUUID())
 			&& LLGiveInventory::isInventoryGiveAcceptable(inv_item))
 		{
 			// *TODO: get multiple object transfers working
 			*accept = ACCEPT_YES_COPY_SINGLE;
-			if (drop)
+			if(drop)
 			{
 				LLIMModel::LLIMSession * session = LLIMModel::instance().findIMSession(session_id);
 
@@ -1553,11 +1582,11 @@ bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_
 	case DAD_CATEGORY:
 	{
 		LLViewerInventoryCategory* inv_cat = (LLViewerInventoryCategory*)cargo_data;
-		if (gInventory.getCategory(inv_cat->getUUID()))
+		if( gInventory.getCategory( inv_cat->getUUID() ) )
 		{
 			// *TODO: get multiple object transfers working
 			*accept = ACCEPT_YES_COPY_SINGLE;
-			if (drop)
+			if(drop)
 			{
 				LLGiveInventory::doGiveInventoryCategory(dest_agent, inv_cat, session_id);
 			}
@@ -1598,7 +1627,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
 {
 	lldebugs << "LLToolDragAndDrop::dad3dRezAttachmentFromInv()" << llendl;
 	// must be in the user's inventory
-	if (mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
+	if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
 	{
 		return ACCEPT_NO;
 	}
@@ -1610,20 +1639,21 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
 
 	// must not be in the trash
 	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-	if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+	if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
 	{
 		return ACCEPT_NO;
 	}
 
 	// must not be already wearing it
-	if (!isAgentAvatarValid() || gAgentAvatarp->isWearingAttachment(item->getUUID()))
+	LLVOAvatarSelf* avatar = gAgentAvatarp;
+	if( !avatar || avatar->isWearingAttachment(item->getUUID()) )
 	{
 		return ACCEPT_NO;
 	}
 
-	if (drop)
+	if( drop )
 	{
-		if (mSource == SOURCE_LIBRARY)
+		if(mSource == SOURCE_LIBRARY)
 		{
 			LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
 			copy_inventory_item(
@@ -1657,7 +1687,8 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
 
-	if (!isAgentAvatarValid() || gAgentAvatarp->isWearingAttachment(item->getUUID()))
+	LLVOAvatarSelf* my_avatar = gAgentAvatarp;
+	if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
 	{
 		return ACCEPT_NO;
 	}
@@ -1682,7 +1713,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
 
 	// check if the item can be copied. If not, send that to the sim
 	// which will remove the inventory item.
-	if (!item->getPermissions().allowCopyBy(gAgent.getID()))
+	if(!item->getPermissions().allowCopyBy(gAgent.getID()))
 	{
 		accept = ACCEPT_YES_SINGLE;
 		remove_inventory = TRUE;
@@ -1690,13 +1721,13 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
 
 	// Check if it's in the trash.
 	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-	if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+	if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
 	{
 		accept = ACCEPT_YES_SINGLE;
 		remove_inventory = TRUE;
 	}
 
-	if (drop)
+	if(drop)
 	{
 		dropObject(obj, TRUE, FALSE, remove_inventory);
 	}
@@ -1718,22 +1749,23 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject(
 	LLViewerInventoryCategory* cat;
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
-	if (!isAgentAvatarValid() || gAgentAvatarp->isWearingAttachment(item->getUUID()))
+	LLVOAvatarSelf* my_avatar = gAgentAvatarp;
+	if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
 	{
 		return ACCEPT_NO;
 	}
 
-	if ((mask & MASK_CONTROL))
+	if((mask & MASK_CONTROL))
 	{
 		// *HACK: In order to resolve SL-22177, we need to block drags
 		// from notecards and objects onto other objects.
-		if (mSource == SOURCE_NOTECARD)
+		if(mSource == SOURCE_NOTECARD)
 		{
 			return ACCEPT_NO;
 		}
 
 		EAcceptance rv = willObjectAcceptInventory(obj, item);
-		if (drop && (ACCEPT_YES_SINGLE <= rv))
+		if(drop && (ACCEPT_YES_SINGLE <= rv))
 		{
 			dropInventory(obj, item, mSource, mSourceID);
 		}
@@ -1759,7 +1791,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject(
 	
 	// check if the item can be copied. If not, send that to the sim
 	// which will remove the inventory item.
-	if (!item->getPermissions().allowCopyBy(gAgent.getID()))
+	if(!item->getPermissions().allowCopyBy(gAgent.getID()))
 	{
 		accept = ACCEPT_YES_SINGLE;
 		remove_inventory = TRUE;
@@ -1767,13 +1799,13 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject(
 
 	// Check if it's in the trash.
 	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-	if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+	if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
 	{
 		accept = ACCEPT_YES_SINGLE;
 		remove_inventory = TRUE;
 	}
 
-	if (drop)
+	if(drop)
 	{
 		dropObject(obj, FALSE, FALSE, remove_inventory);
 	}
@@ -1788,7 +1820,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezScript(
 
 	// *HACK: In order to resolve SL-22177, we need to block drags
 	// from notecards and objects onto other objects.
-	if ((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+	if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
 	{
 		return ACCEPT_NO;
 	}
@@ -1798,7 +1830,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezScript(
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
 	EAcceptance rv = willObjectAcceptInventory(obj, item);
-	if (drop && (ACCEPT_YES_SINGLE <= rv))
+	if(drop && (ACCEPT_YES_SINGLE <= rv))
 	{
 		// rez in the script active by default, rez in inactive if the
 		// control key is being held down.
@@ -1819,14 +1851,14 @@ EAcceptance LLToolDragAndDrop::dad3dRezScript(
 	return rv;
 }
 
-EAcceptance LLToolDragAndDrop::dad3dTextureObject(
-	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+EAcceptance LLToolDragAndDrop::dad3dApplyToObject(
+	LLViewerObject* obj, S32 face, MASK mask, BOOL drop, EDragAndDropType cargo_type)
 {
-	lldebugs << "LLToolDragAndDrop::dad3dTextureObject()" << llendl;
+	lldebugs << "LLToolDragAndDrop::dad3dApplyToObject()" << llendl;
 
 	// *HACK: In order to resolve SL-22177, we need to block drags
 	// from notecards and objects onto other objects.
-	if ((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+	if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
 	{
 		return ACCEPT_NO;
 	}
@@ -1836,33 +1868,44 @@ EAcceptance LLToolDragAndDrop::dad3dTextureObject(
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
 	EAcceptance rv = willObjectAcceptInventory(obj, item);
-	if ((mask & MASK_CONTROL))
+	if((mask & MASK_CONTROL))
 	{
-		if ((ACCEPT_YES_SINGLE <= rv) && drop)
+		if((ACCEPT_YES_SINGLE <= rv) && drop)
 		{
 			dropInventory(obj, item, mSource, mSourceID);
 		}
 		return rv;
 	}
-	if (!obj->permModify())
+	if(!obj->permModify())
 	{
 		return ACCEPT_NO_LOCKED;
 	}
 	//If texture !copyable don't texture or you'll never get it back.
-	if (!item->getPermissions().allowCopyBy(gAgent.getID()))
+	if(!item->getPermissions().allowCopyBy(gAgent.getID()))
 	{
 		return ACCEPT_NO;
 	}
 
-	if (drop && (ACCEPT_YES_SINGLE <= rv))
+	if(drop && (ACCEPT_YES_SINGLE <= rv))
 	{
-		if ((mask & MASK_SHIFT))
+		if (cargo_type == DAD_TEXTURE)
 		{
-			dropTextureAllFaces(obj, item, mSource, mSourceID);
+			if((mask & MASK_SHIFT))
+			{
+				dropTextureAllFaces(obj, item, mSource, mSourceID);
+			}
+			else
+			{
+				dropTextureOneFace(obj, face, item, mSource, mSourceID);
+			}
+		}
+		else if (cargo_type == DAD_MESH)
+		{
+			dropMesh(obj, item, mSource, mSourceID);
 		}
 		else
 		{
-			dropTextureOneFace(obj, face, item, mSource, mSourceID);
+			llwarns << "unsupported asset type" << llendl;
 		}
 		
 		// VEFFECT: SetTexture
@@ -1876,14 +1919,29 @@ EAcceptance LLToolDragAndDrop::dad3dTextureObject(
 	// enable multi-drop, although last texture will win
 	return ACCEPT_YES_MULTI;
 }
+
+
+EAcceptance LLToolDragAndDrop::dad3dTextureObject(
+	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+	return dad3dApplyToObject(obj, face, mask, drop, DAD_TEXTURE);
+}
+
+EAcceptance LLToolDragAndDrop::dad3dMeshObject(
+	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+	return dad3dApplyToObject(obj, face, mask, drop, DAD_MESH);
+}
+
+
 /*
 EAcceptance LLToolDragAndDrop::dad3dTextureSelf(
 	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
 {
 	lldebugs << "LLToolDragAndDrop::dad3dTextureAvatar()" << llendl;
-	if (drop)
+	if(drop)
 	{
-		if (!(mask & MASK_SHIFT))
+		if( !(mask & MASK_SHIFT) )
 		{
 			dropTextureOneFaceAvatar( (LLVOAvatar*)obj, face, (LLInventoryItem*)mCargoData);
 		}
@@ -1901,16 +1959,16 @@ EAcceptance LLToolDragAndDrop::dad3dWearItem(
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
 
-	if (mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
+	if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
 	{
 		// it's in the agent inventory
 		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+		if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
 		{
 			return ACCEPT_NO;
 		}
 
-		if (drop)
+		if( drop )
 		{
 			// TODO: investigate wearables may not be loaded at this point EXT-8231
 
@@ -1934,19 +1992,19 @@ EAcceptance LLToolDragAndDrop::dad3dActivateGesture(
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
 
-	if (mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
+	if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
 	{
 		// it's in the agent inventory
 		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+		if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
 		{
 			return ACCEPT_NO;
 		}
 
-		if (drop)
+		if( drop )
 		{
 			LLUUID item_id;
-			if (mSource == SOURCE_LIBRARY)
+			if(mSource == SOURCE_LIBRARY)
 			{
 				// create item based on that one, and put it on if that
 				// was a success.
@@ -1981,31 +2039,31 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory(
 	LLViewerInventoryItem* item;
 	LLViewerInventoryCategory* category;
 	locateInventory(item, category);
-	if (!category) return ACCEPT_NO;
+	if(!category) return ACCEPT_NO;
 
 	if (drop)
 	{
 		// TODO: investigate wearables may not be loaded at this point EXT-8231
 	}
 
-	if (mSource == SOURCE_AGENT)
+	if(mSource == SOURCE_AGENT)
 	{
 		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		if (gInventory.isObjectDescendentOf(category->getUUID(), trash_id))
+		if( gInventory.isObjectDescendentOf( category->getUUID(), trash_id ) )
 		{
 			return ACCEPT_NO;
 		}
 
-		if (drop)
+		if(drop)
 		{
-		    BOOL append = ( (mask & MASK_SHIFT) ? TRUE : FALSE );
+			BOOL append = ( (mask & MASK_SHIFT) ? TRUE : FALSE );
 			LLAppearanceMgr::instance().wearInventoryCategory(category, false, append);
 		}
 		return ACCEPT_YES_MULTI;
 	}
-	else if (mSource == SOURCE_LIBRARY)
+	else if(mSource == SOURCE_LIBRARY)
 	{
-		if (drop)
+		if(drop)
 		{
 			LLAppearanceMgr::instance().wearInventoryCategory(category, true, false);
 		}
@@ -2026,7 +2084,7 @@ EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
 
 	// *HACK: In order to resolve SL-22177, we need to block drags
 	// from notecards and objects onto other objects.
-	if ((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+	if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
 	{
 		return ACCEPT_NO;
 	}
@@ -2046,7 +2104,7 @@ EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
 	}
 
 	EAcceptance rv = willObjectAcceptInventory(root_object, item);
-	if (root_object && drop && (ACCEPT_YES_COPY_SINGLE <= rv))
+	if(root_object && drop && (ACCEPT_YES_COPY_SINGLE <= rv))
 	{
 		dropInventory(root_object, item, mSource, mSourceID);
 	}
@@ -2090,7 +2148,7 @@ EAcceptance LLToolDragAndDrop::dad3dUpdateInventoryCategory(
 	LLDroppableItem droppable(!obj->permYouOwner());
 	LLInventoryModel::cat_array_t cats;
 	LLInventoryModel::item_array_t items;
-	gInventory.collectDescendentsIf (cat->getUUID(),
+	gInventory.collectDescendentsIf(cat->getUUID(),
 					cats,
 					items,
 					LLInventoryModel::EXCLUDE_TRASH,
@@ -2119,7 +2177,7 @@ EAcceptance LLToolDragAndDrop::dad3dUpdateInventoryCategory(
 	{
 		const LLViewerInventoryCategory *cat = (*cat_iter);
 		rv = gInventory.isCategoryComplete(cat->getUUID()) ? ACCEPT_YES_MULTI : ACCEPT_NO;
-		if (rv < ACCEPT_YES_SINGLE)
+		if(rv < ACCEPT_YES_SINGLE)
 		{
 			lldebugs << "Category " << cat->getUUID() << "is not complete." << llendl;
 			break;
@@ -2187,26 +2245,27 @@ EAcceptance LLToolDragAndDrop::dad3dGiveInventoryObject(
 	lldebugs << "LLToolDragAndDrop::dad3dGiveInventoryObject()" << llendl;
 
 	// item has to be in agent inventory.
-	if (mSource != SOURCE_AGENT) return ACCEPT_NO;
+	if(mSource != SOURCE_AGENT) return ACCEPT_NO;
 
 	// find the item now.
 	LLViewerInventoryItem* item;
 	LLViewerInventoryCategory* cat;
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
-	if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+	if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
 	{
 		// cannot give away no-transfer objects
 		return ACCEPT_NO;
 	}
-	if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getUUID()))
+	LLVOAvatarSelf* avatar = gAgentAvatarp;
+	if(avatar && avatar->isWearingAttachment( item->getUUID() ) )
 	{
 		// You can't give objects that are attached to you
 		return ACCEPT_NO;
 	}
-	if (obj && isAgentAvatarValid())
+	if( obj && avatar )
 	{
-		if (drop)
+		if(drop)
 		{
 			LLGiveInventory::doGiveInventoryItem(obj->getID(), item );
 		}
@@ -2223,7 +2282,7 @@ EAcceptance LLToolDragAndDrop::dad3dGiveInventory(
 {
 	lldebugs << "LLToolDragAndDrop::dad3dGiveInventory()" << llendl;
 	// item has to be in agent inventory.
-	if (mSource != SOURCE_AGENT) return ACCEPT_NO;
+	if(mSource != SOURCE_AGENT) return ACCEPT_NO;
 	LLViewerInventoryItem* item;
 	LLViewerInventoryCategory* cat;
 	locateInventory(item, cat);
@@ -2245,12 +2304,12 @@ EAcceptance LLToolDragAndDrop::dad3dGiveInventoryCategory(
 	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
 {
 	lldebugs << "LLToolDragAndDrop::dad3dGiveInventoryCategory()" << llendl;
-	if (drop && obj)
+	if(drop && obj)
 	{
 		LLViewerInventoryItem* item;
 		LLViewerInventoryCategory* cat;
 		locateInventory(item, cat);
-		if (!cat) return ACCEPT_NO;
+		if(!cat) return ACCEPT_NO;
 		LLGiveInventory::doGiveInventoryCategory(obj->getID(), cat);
 	}
 	// *TODO: deal with all the issues surrounding multi-object
@@ -2268,12 +2327,12 @@ EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnLand(
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
 
-	if (!gAgent.allowOperation(PERM_COPY, item->getPermissions())
+	if(!gAgent.allowOperation(PERM_COPY, item->getPermissions())
 		|| !item->getPermissions().allowTransferTo(LLUUID::null))
 	{
 		return ACCEPT_NO_LOCKED;
 	}
-	if (drop)
+	if(drop)
 	{
 		dropObject(obj, TRUE, TRUE, FALSE);
 	}
@@ -2288,7 +2347,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnObject(
 	LLViewerInventoryCategory* cat;
 	locateInventory(item, cat);
 	if (!item || !item->isFinished()) return ACCEPT_NO;
-	if ((mask & MASK_CONTROL))
+	if((mask & MASK_CONTROL))
 	{
 		// *HACK: In order to resolve SL-22177, we need to block drags
 		// from notecards and objects onto other objects.
@@ -2296,19 +2355,19 @@ EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnObject(
 
 		// *HACK: uncomment this when appropriate
 		//EAcceptance rv = willObjectAcceptInventory(obj, item);
-		//if (drop && (ACCEPT_YES_SINGLE <= rv))
+		//if(drop && (ACCEPT_YES_SINGLE <= rv))
 		//{
 		//	dropInventory(obj, item, mSource, mSourceID);
 		//}
 		//return rv;
 	}
-	if (!item->getPermissions().allowCopyBy(gAgent.getID(),
+	if(!item->getPermissions().allowCopyBy(gAgent.getID(),
 										   gAgent.getGroupID())
 	   || !item->getPermissions().allowTransferTo(LLUUID::null))
 	{
 		return ACCEPT_NO_LOCKED;
 	}
-	if (drop)
+	if(drop)
 	{
 		dropObject(obj, FALSE, TRUE, FALSE);
 	}
@@ -2324,23 +2383,23 @@ EAcceptance LLToolDragAndDrop::dad3dCategoryOnLand(
 	LLInventoryItem* item;
 	LLInventoryCategory* cat;
 	locateInventory(item, cat);
-	if (!cat) return ACCEPT_NO;
+	if(!cat) return ACCEPT_NO;
 	EAcceptance rv = ACCEPT_NO;
 
 	// find all the items in the category
 	LLViewerInventoryCategory::cat_array_t cats;
 	LLViewerInventoryItem::item_array_t items;
 	LLDropCopyableItems droppable;
-	gInventory.collectDescendentsIf (cat->getUUID(),
+	gInventory.collectDescendentsIf(cat->getUUID(),
 									cats,
 									items,
 									LLInventoryModel::EXCLUDE_TRASH,
 									droppable);
-	if (items.count() > 0)
+	if(items.count() > 0)
 	{
 		rv = ACCEPT_YES_SINGLE;
 	}
-	if ((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
+	if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
 	{
 		createContainer(items, cat->getName());
 		return ACCEPT_NO;
@@ -2363,19 +2422,19 @@ EAcceptance LLToolDragAndDrop::dad3dAssetOnLand(
 	LLViewerInventoryItem::item_array_t items;
 	LLViewerInventoryItem::item_array_t copyable_items;
 	locateMultipleInventory(items, cats);
-	if (!items.count()) return ACCEPT_NO;
+	if(!items.count()) return ACCEPT_NO;
 	EAcceptance rv = ACCEPT_NO;
 	for (S32 i = 0; i < items.count(); i++)
 	{
 		LLInventoryItem* item = items[i];
-		if (item->getPermissions().allowCopyBy(gAgent.getID()))
+		if(item->getPermissions().allowCopyBy(gAgent.getID()))
 		{
 			copyable_items.put(item);
 			rv = ACCEPT_YES_SINGLE;
 		}
 	}
 
-	if ((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
+	if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
 	{
 		createContainer(copyable_items, NULL);
 	}
@@ -2390,20 +2449,20 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory(
 {
 	item = NULL;
 	cat = NULL;
-	if (mCargoIDs.empty()) return NULL;
-	if ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+	if(mCargoIDs.empty()) return NULL;
+	if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
 	{
 		// The object should be in user inventory.
 		item = (LLViewerInventoryItem*)gInventory.getItem(mCargoIDs[mCurItemIndex]);
 		cat = (LLViewerInventoryCategory*)gInventory.getCategory(mCargoIDs[mCurItemIndex]);
 	}
-	else if (mSource == SOURCE_WORLD)
+	else if(mSource == SOURCE_WORLD)
 	{
 		// This object is in some task inventory somewhere.
 		LLViewerObject* obj = gObjectList.findObject(mSourceID);
-		if (obj)
+		if(obj)
 		{
-			if ((mCargoTypes[mCurItemIndex] == DAD_CATEGORY)
+			if((mCargoTypes[mCurItemIndex] == DAD_CATEGORY)
 			   || (mCargoTypes[mCurItemIndex] == DAD_ROOT_CATEGORY))
 			{
 				cat = (LLViewerInventoryCategory*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
@@ -2414,16 +2473,16 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory(
 			}
 		}
 	}
-	else if (mSource == SOURCE_NOTECARD)
+	else if(mSource == SOURCE_NOTECARD)
 	{
 		LLPreviewNotecard* preview = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", mSourceID);
-		if (preview)
+		if(preview)
 		{
 			item = (LLViewerInventoryItem*)preview->getDragItem();
 		}
 	}
-	if (item) return item;
-	if (cat) return cat;
+	if(item) return item;
+	if(cat) return cat;
 	return NULL;
 }
 
@@ -2431,8 +2490,8 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory(
 LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryCategory::cat_array_t& cats,
 															  LLViewerInventoryItem::item_array_t& items)
 {
-	if (mCargoIDs.count() == 0) return NULL;
-	if ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+	if(mCargoIDs.count() == 0) return NULL;
+	if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
 	{
 		// The object should be in user inventory.
 		for (S32 i = 0; i < mCargoIDs.count(); i++)
@@ -2449,13 +2508,13 @@ LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryC
 			}
 		}
 	}
-	else if (mSource == SOURCE_WORLD)
+	else if(mSource == SOURCE_WORLD)
 	{
 		// This object is in some task inventory somewhere.
 		LLViewerObject* obj = gObjectList.findObject(mSourceID);
-		if (obj)
+		if(obj)
 		{
-			if ((mCargoType == DAD_CATEGORY)
+			if((mCargoType == DAD_CATEGORY)
 			   || (mCargoType == DAD_ROOT_CATEGORY))
 			{
 				// The object should be in user inventory.
@@ -2481,17 +2540,17 @@ LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryC
 			}
 		}
 	}
-	else if (mSource == SOURCE_NOTECARD)
+	else if(mSource == SOURCE_NOTECARD)
 	{
 		LLPreviewNotecard* card;
 		card = (LLPreviewNotecard*)LLPreview::find(mSourceID);
-		if (card)
+		if(card)
 		{
 			items.put((LLInventoryItem*)card->getDragItem());
 		}
 	}
-	if (items.count()) return items[0];
-	if (cats.count()) return cats[0];
+	if(items.count()) return items[0];
+	if(cats.count()) return cats[0];
 	return NULL;
 }
 */
diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h
index a13b775699cebde6a6d7878ea22329fd95ef40a1..7b8cce3dc7f0c6e7fbbc2fd30f40e0bd77311724 100644
--- a/indra/newview/lltooldraganddrop.h
+++ b/indra/newview/lltooldraganddrop.h
@@ -148,6 +148,8 @@ class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop>
 							   MASK mask, BOOL drop);
 	EAcceptance dad3dTextureObject(LLViewerObject* obj, S32 face,
 								   MASK mask, BOOL drop);
+	EAcceptance dad3dMeshObject(LLViewerObject* obj, S32 face,
+								   MASK mask, BOOL drop);
 //	EAcceptance dad3dTextureSelf(LLViewerObject* obj, S32 face,
 //								 MASK mask, BOOL drop);
 	EAcceptance dad3dWearItem(LLViewerObject* obj, S32 face,
@@ -179,6 +181,11 @@ class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop>
 	EAcceptance dad3dActivateGesture(LLViewerObject *obj, S32 face,
 								 MASK mask, BOOL drop);
 
+	// helper called by methods above to handle "application" of an item
+	// to an object (texture applied to face, mesh applied to shape, etc.)
+	EAcceptance dad3dApplyToObject(LLViewerObject* obj, S32 face, MASK mask, BOOL drop, EDragAndDropType cargo_type);
+		
+	
 	// set the LLToolDragAndDrop's cursor based on the given acceptance
 	ECursorType acceptanceToCursor( EAcceptance acceptance );
 
@@ -229,6 +236,11 @@ class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop>
 									LLInventoryItem* item,
 									ESource source,
 									const LLUUID& src_id);
+	static void dropMesh(LLViewerObject* hit_obj,
+						 LLInventoryItem* item,
+						 ESource source,
+						 const LLUUID& src_id);
+	
 	//static void	dropTextureOneFaceAvatar(LLVOAvatar* avatar,S32 hit_face,
 	//									 LLInventoryItem* item)
 
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
index 7024b2c78515c4a84c25fc3d8681350b21db697c..e621cf647ef777f1354e43fb0dceb9854c7a5304 100644
--- a/indra/newview/llviewerassetstats.cpp
+++ b/indra/newview/llviewerassetstats.cpp
@@ -525,7 +525,7 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i
 	//  - gestures
 	//  - everything else.
 	//
-	llassert_always(26 == LLViewerAssetType::AT_COUNT);
+	llassert_always(50 == LLViewerAssetType::AT_COUNT);
 
 	// Multiple asset definitions are floating around so this requires some
 	// maintenance and attention.
@@ -557,9 +557,7 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i
 			LLViewerAssetStats::EVACOtherGet,					// AT_FAVORITE
 			LLViewerAssetStats::EVACOtherGet,					// AT_LINK
 			LLViewerAssetStats::EVACOtherGet,					// AT_LINK_FOLDER
-#if 0
-			// When LLViewerAssetType::AT_COUNT == 49
-			LLViewerAssetStats::EVACOtherGet,					// AT_FOLDER_ENSEMBLE_START
+			LLViewerAssetStats::EVACOtherGet,					// 
 			LLViewerAssetStats::EVACOtherGet,					// 
 			LLViewerAssetStats::EVACOtherGet,					// 
 			LLViewerAssetStats::EVACOtherGet,					// 
@@ -578,11 +576,12 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i
 			LLViewerAssetStats::EVACOtherGet,					// 
 			LLViewerAssetStats::EVACOtherGet,					// 
 			LLViewerAssetStats::EVACOtherGet,					// 
-			LLViewerAssetStats::EVACOtherGet,					// AT_FOLDER_ENSEMBLE_END
-			LLViewerAssetStats::EVACOtherGet,					// AT_CURRENT_OUTFIT
-			LLViewerAssetStats::EVACOtherGet,					// AT_OUTFIT
-			LLViewerAssetStats::EVACOtherGet					// AT_MY_OUTFITS
-#endif
+			LLViewerAssetStats::EVACOtherGet,					//
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// AT_MESH
+																// (50)
 		};
 	
 	if (at < 0 || at >= LLViewerAssetType::AT_COUNT)
diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp
index 9a52422641a3e4be736ba450d12c43f3c914e0d4..b103f115976cd1943e4fa39323737d7f143d59bf 100644
--- a/indra/newview/llviewerassettype.cpp
+++ b/indra/newview/llviewerassettype.cpp
@@ -79,6 +79,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary()
 	addEntry(LLViewerAssetType::AT_LINK, 				new ViewerAssetEntry(DAD_LINK));
 	addEntry(LLViewerAssetType::AT_LINK_FOLDER, 		new ViewerAssetEntry(DAD_LINK));
 
+	addEntry(LLViewerAssetType::AT_MESH, 				new ViewerAssetEntry(DAD_MESH));
+
 	addEntry(LLViewerAssetType::AT_NONE, 				new ViewerAssetEntry(DAD_NONE));
 };
 
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index cbb1d25f78e51609ad0d4a69900cb5f1ff2d5ea8..7f7366dd3d9ec50b2cf857c28c80e2c05e0938e3 100644
--- a/indra/newview/llviewercamera.cpp
+++ b/indra/newview/llviewercamera.cpp
@@ -32,6 +32,7 @@
 // Viewer includes
 #include "llagent.h"
 #include "llagentcamera.h"
+#include "llmatrix4a.h"
 #include "llviewercontrol.h"
 #include "llviewerobjectlist.h"
 #include "llviewerregion.h"
@@ -755,6 +756,10 @@ LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
 
 BOOL LLViewerCamera::cameraUnderWater() const
 {
+	if(!gAgent.getRegion())
+	{
+		return FALSE ;
+	}
 	return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
 }
 
@@ -781,21 +786,29 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
 	
 	LLMatrix4 render_mat(vo_volume->getRenderRotation(), LLVector4(vo_volume->getRenderPosition()));
 
+	LLMatrix4a render_mata;
+	render_mata.loadu(render_mat);
+	LLMatrix4a mata;
+	mata.loadu(mat);
+
 	num_faces = volume->getNumVolumeFaces();
 	for (i = 0; i < num_faces; i++)
 	{
 		const LLVolumeFace& face = volume->getVolumeFace(i);
 				
-		for (U32 v = 0; v < face.mVertices.size(); v++)
+		for (U32 v = 0; v < face.mNumVertices; v++)
 		{
-			LLVector4 vec = LLVector4(face.mVertices[v].mPosition) * mat;
+			const LLVector4a& src_vec = face.mPositions[v];
+			LLVector4a vec;
+			mata.affineTransform(src_vec, vec);
 
 			if (drawablep->isActive())
 			{
-				vec = vec * render_mat;	
+				LLVector4a t = vec;
+				render_mata.affineTransform(t, vec);
 			}
 
-			BOOL in_frustum = pointInFrustum(LLVector3(vec)) > 0;
+			BOOL in_frustum = pointInFrustum(LLVector3(vec.getF32ptr())) > 0;
 
 			if (( !in_frustum && all_verts) ||
 				 (in_frustum && !all_verts))
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 06c152031460c550a49325bce328c243b3b49b0d..379bbe614d62161a5d83a0cabd4cca2830c4db84 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -128,6 +128,45 @@ static bool handleSetShaderChanged(const LLSD& newvalue)
 	return true;
 }
 
+static bool handleRenderPerfTestChanged(const LLSD& newvalue)
+{
+       bool status = !newvalue.asBoolean();
+       if (!status)
+       {
+               gPipeline.clearRenderTypeMask(LLPipeline::RENDER_TYPE_WL_SKY,
+                                                                         LLPipeline::RENDER_TYPE_GROUND,
+                                                                        LLPipeline::RENDER_TYPE_TERRAIN,
+                                                                         LLPipeline::RENDER_TYPE_GRASS,
+                                                                         LLPipeline::RENDER_TYPE_TREE,
+                                                                         LLPipeline::RENDER_TYPE_WATER,
+                                                                         LLPipeline::RENDER_TYPE_PASS_GRASS,
+                                                                         LLPipeline::RENDER_TYPE_HUD,
+                                                                         LLPipeline::RENDER_TYPE_PARTICLES,
+                                                                         LLPipeline::RENDER_TYPE_CLOUDS,
+                                                                         LLPipeline::RENDER_TYPE_HUD_PARTICLES,
+                                                                         LLPipeline::END_RENDER_TYPES); 
+               gPipeline.setRenderDebugFeatureControl(LLPipeline::RENDER_DEBUG_FEATURE_UI, false);
+       }
+       else 
+       {
+               gPipeline.setRenderTypeMask(LLPipeline::RENDER_TYPE_WL_SKY,
+                                                                         LLPipeline::RENDER_TYPE_GROUND,
+                                                                         LLPipeline::RENDER_TYPE_TERRAIN,
+                                                                         LLPipeline::RENDER_TYPE_GRASS,
+                                                                         LLPipeline::RENDER_TYPE_TREE,
+                                                                         LLPipeline::RENDER_TYPE_WATER,
+                                                                         LLPipeline::RENDER_TYPE_PASS_GRASS,
+                                                                         LLPipeline::RENDER_TYPE_HUD,
+                                                                         LLPipeline::RENDER_TYPE_PARTICLES,
+                                                                         LLPipeline::RENDER_TYPE_CLOUDS,
+                                                                         LLPipeline::RENDER_TYPE_HUD_PARTICLES,
+                                                                         LLPipeline::END_RENDER_TYPES);
+               gPipeline.setRenderDebugFeatureControl(LLPipeline::RENDER_DEBUG_FEATURE_UI, true);
+       }
+
+       return true;
+}
+
 bool handleRenderTransparentWaterChanged(const LLSD& newvalue)
 {
 	LLWorld::getInstance()->updateWaterObjects();
@@ -300,24 +339,6 @@ static bool handleNumpadControlChanged(const LLSD& newvalue)
 	return true;
 }
 
-static bool handleRenderUseVBOChanged(const LLSD& newvalue)
-{
-	if (gPipeline.isInit())
-	{
-		gPipeline.setUseVBO(newvalue.asBoolean());
-	}
-	return true;
-}
-
-static bool handleRenderUseVBOMappingChanged(const LLSD& newvalue)
-{
-	if (gPipeline.isInit())
-	{
-		gPipeline.setDisableVBOMapping(newvalue.asBoolean());
-	}
-	return true;
-}
-
 static bool handleWLSkyDetailChanged(const LLSD&)
 {
 	if (gSky.mVOWLSkyp.notNull())
@@ -342,13 +363,21 @@ static bool handleRenderDynamicLODChanged(const LLSD& newvalue)
 	return true;
 }
 
-static bool handleRenderUseFBOChanged(const LLSD& newvalue)
+static bool handleRenderLocalLightsChanged(const LLSD& newvalue)
+{
+	gPipeline.setLightingDetail(-1);
+	return true;
+}
+
+static bool handleRenderDeferredChanged(const LLSD& newvalue)
 {
 	LLRenderTarget::sUseFBO = newvalue.asBoolean();
 	if (gPipeline.isInit())
 	{
+		gPipeline.updateRenderDeferred();
 		gPipeline.releaseGLBuffers();
 		gPipeline.createGLBuffers();
+		gPipeline.resetVertexBuffers();
 		if (LLPipeline::sRenderDeferred && LLRenderTarget::sUseFBO)
 		{
 			LLViewerShaderMgr::instance()->setShaders();
@@ -560,21 +589,22 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("RenderFogRatio")->getSignal()->connect(boost::bind(&handleFogRatioChanged, _2));
 	gSavedSettings.getControl("RenderMaxPartCount")->getSignal()->connect(boost::bind(&handleMaxPartCountChanged, _2));
 	gSavedSettings.getControl("RenderDynamicLOD")->getSignal()->connect(boost::bind(&handleRenderDynamicLODChanged, _2));
+	gSavedSettings.getControl("RenderLocalLights")->getSignal()->connect(boost::bind(&handleRenderLocalLightsChanged, _2));
 	gSavedSettings.getControl("RenderDebugTextureBind")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderAutoMaskAlphaDeferred")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderAutoMaskAlphaNonDeferred")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderObjectBump")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderMaxVBOSize")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderUseFBO")->getSignal()->connect(boost::bind(&handleRenderUseFBOChanged, _2));
 	gSavedSettings.getControl("RenderDeferredNoise")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
 	gSavedSettings.getControl("RenderUseImpostors")->getSignal()->connect(boost::bind(&handleRenderUseImpostorsChanged, _2));
 	gSavedSettings.getControl("RenderDebugGL")->getSignal()->connect(boost::bind(&handleRenderDebugGLChanged, _2));
 	gSavedSettings.getControl("RenderDebugPipeline")->getSignal()->connect(boost::bind(&handleRenderDebugPipelineChanged, _2));
 	gSavedSettings.getControl("RenderResolutionDivisor")->getSignal()->connect(boost::bind(&handleRenderResolutionDivisorChanged, _2));
-	gSavedSettings.getControl("RenderDeferred")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
+	gSavedSettings.getControl("RenderDeferred")->getSignal()->connect(boost::bind(&handleRenderDeferredChanged, _2));
 	gSavedSettings.getControl("RenderShadowDetail")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
 	gSavedSettings.getControl("RenderDeferredSSAO")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
 	gSavedSettings.getControl("RenderDeferredGI")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
+	gSavedSettings.getControl("RenderPerformanceTest")->getSignal()->connect(boost::bind(&handleRenderPerfTestChanged, _2));
 	gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2));
 	gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _2));
 	gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&handleChatFontSizeChanged, _2));
@@ -597,9 +627,10 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("MuteVoice")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
 	gSavedSettings.getControl("MuteAmbient")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
 	gSavedSettings.getControl("MuteUI")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("RenderVBOEnable")->getSignal()->connect(boost::bind(&handleRenderUseVBOChanged, _2));
-	gSavedSettings.getControl("RenderVBOMappingDisable")->getSignal()->connect(boost::bind(&handleRenderUseVBOMappingChanged, _2));
+	gSavedSettings.getControl("RenderVBOEnable")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
+	gSavedSettings.getControl("RenderVBOMappingDisable")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderUseStreamVBO")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
+	gSavedSettings.getControl("RenderPreferStreamDraw")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("WLSkyDetail")->getSignal()->connect(boost::bind(&handleWLSkyDetailChanged, _2));
 	gSavedSettings.getControl("NumpadControl")->getSignal()->connect(boost::bind(&handleNumpadControlChanged, _2));
 	gSavedSettings.getControl("JoystickAxis0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 8593c4cf79055c6d037932db299b2c80f0742b8e..e41773d27365d3d986fcd7bdb7d805b6adc90d37 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -616,10 +616,10 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 				&& gSavedSettings.getBOOL("UseOcclusion") 
 				&& gGLManager.mHasOcclusionQuery) ? 2 : 0;
 
-		if (LLPipeline::sUseOcclusion && LLPipeline::sRenderDeferred)
-		{ //force occlusion on for all render types if doing deferred render
+		/*if (LLPipeline::sUseOcclusion && LLPipeline::sRenderDeferred)
+		{ //force occlusion on for all render types if doing deferred render (tighter shadow frustum)
 			LLPipeline::sUseOcclusion = 3;
-		}
+		}*/
 
 		LLPipeline::sAutoMaskAlphaDeferred = gSavedSettings.getBOOL("RenderAutoMaskAlphaDeferred");
 		LLPipeline::sAutoMaskAlphaNonDeferred = gSavedSettings.getBOOL("RenderAutoMaskAlphaNonDeferred");
@@ -754,7 +754,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 		{
 			LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD;
 			LLMemType mt_ss(LLMemType::MTYPE_DISPLAY_STATE_SORT);
-			gPipeline.sAllowRebuildPriorityGroup = TRUE ;
 			gPipeline.stateSort(*LLViewerCamera::getInstance(), result);
 			stop_glerror();
 				
@@ -826,13 +825,14 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 		//}
 
 		LLPipeline::sUnderWaterRender = LLViewerCamera::getInstance()->cameraUnderWater() ? TRUE : FALSE;
-		LLPipeline::updateRenderDeferred();
+		LLPipeline::refreshRenderDeferred();
 		
 		stop_glerror();
 
 		if (to_texture)
 		{
 			gGL.setColorMask(true, true);
+					
 			if (LLPipeline::sRenderDeferred && !LLPipeline::sUnderWaterRender)
 			{
 				gPipeline.mDeferredScreen.bindTarget();
@@ -885,10 +885,26 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 			if (LLPipeline::sRenderDeferred && !LLPipeline::sUnderWaterRender)
 			{
 				gPipeline.mDeferredScreen.flush();
+				if(LLRenderTarget::sUseFBO)
+				{
+					LLRenderTarget::copyContentsToFramebuffer(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), 
+															  gPipeline.mDeferredScreen.getHeight(), 0, 0, 
+															  gPipeline.mDeferredScreen.getWidth(), 
+															  gPipeline.mDeferredScreen.getHeight(), 
+															  GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+				}
 			}
 			else
 			{
 				gPipeline.mScreen.flush();
+				if(LLRenderTarget::sUseFBO)
+				{				
+					LLRenderTarget::copyContentsToFramebuffer(gPipeline.mScreen, 0, 0, gPipeline.mScreen.getWidth(), 
+															  gPipeline.mScreen.getHeight(), 0, 0, 
+															  gPipeline.mScreen.getWidth(), 
+															  gPipeline.mScreen.getHeight(), 
+															  GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+				}
 			}
 		}
 
@@ -906,9 +922,11 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 			render_ui();
 		}
 
-		gPipeline.rebuildGroups();
-
+		
 		LLSpatialGroup::sNoDelete = FALSE;
+		gPipeline.clearReferences();
+
+		gPipeline.rebuildGroups();
 	}
 
 	LLAppViewer::instance()->pingMainloopTimeout("Display:FrameStats");
@@ -1006,6 +1024,7 @@ void render_hud_attachments()
 		gPipeline.renderGeom(hud_cam);
 
 		LLSpatialGroup::sNoDelete = FALSE;
+		//gPipeline.clearReferences();
 
 		render_hud_elements();
 
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 6dc85799ce2dbaa1bca1f39d2477a8ff9278eaf0..a1c2c926af55786d0f5b55b309065e7f751ba964 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -73,6 +73,7 @@
 #include "llfloaterlandholdings.h"
 #include "llfloatermap.h"
 #include "llfloatermemleak.h"
+#include "llfloatermodelwizard.h"
 #include "llfloaternamedesc.h"
 #include "llfloaternotificationsconsole.h"
 #include "llfloateropenobject.h"
@@ -123,8 +124,34 @@
 #include "llpreviewtexture.h"
 #include "llsyswellwindow.h"
 #include "llscriptfloater.h"
+#include "llfloatermodelpreview.h"
+#include "llcommandhandler.h"
+
 // *NOTE: Please add files in alphabetical order to keep merges easy.
 
+// handle secondlife:///app/floater/{NAME} URLs
+class LLFloaterOpenHandler : public LLCommandHandler
+{
+public:
+	// requires trusted browser to trigger
+	LLFloaterOpenHandler() : LLCommandHandler("floater", UNTRUSTED_THROTTLE) { }
+
+	bool handle(const LLSD& params, const LLSD& query_map,
+				LLMediaCtrl* web)
+	{
+		if (params.size() != 1)
+		{
+			return false;
+		}
+
+		const std::string floater_name = LLURI::unescape(params[0].asString());
+		LLFloaterReg::showInstance(floater_name);
+
+		return true;
+	}
+};
+
+LLFloaterOpenHandler gFloaterOpenHandler;
 
 void LLViewerFloaterReg::registerFloaters()
 {
@@ -251,7 +278,9 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("upload_anim", "floater_animation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAnimPreview>, "upload");
 	LLFloaterReg::add("upload_image", "floater_image_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterImagePreview>, "upload");
 	LLFloaterReg::add("upload_sound", "floater_sound_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundPreview>, "upload");
-	
+	LLFloaterReg::add("upload_model", "floater_model_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterModelPreview>, "upload");
+	LLFloaterReg::add("upload_model_wizard", "floater_model_wizard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterModelWizard>);
+
 	LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLCallFloater>);
 	LLFloaterReg::add("voice_effect", "floater_voice_effect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterVoiceEffect>);
 
diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp
index de1c8d14a8679942c8bc2b66a05873fdafbee30d..42f780a8a3af5c687d367a13ef4e3031153f97ec 100644
--- a/indra/newview/llviewerfoldertype.cpp
+++ b/indra/newview/llviewerfoldertype.cpp
@@ -126,6 +126,9 @@ LLViewerFolderDictionary::LLViewerFolderDictionary()
 	addEntry(LLFolderType::FT_CURRENT_OUTFIT, 		new ViewerFolderEntry("Current Outfit",			"Inv_SysOpen",			"Inv_SysClosed",		TRUE));
 	addEntry(LLFolderType::FT_OUTFIT, 				new ViewerFolderEntry("New Outfit",				"Inv_LookFolderOpen",	"Inv_LookFolderClosed",	TRUE));
 	addEntry(LLFolderType::FT_MY_OUTFITS, 			new ViewerFolderEntry("My Outfits",				"Inv_SysOpen",			"Inv_SysClosed",		TRUE));
+	addEntry(LLFolderType::FT_MESH, 				new ViewerFolderEntry("Meshes",					"Inv_SysOpen",			"Inv_SysClosed",		FALSE));
+	
+
 	addEntry(LLFolderType::FT_INBOX, 				new ViewerFolderEntry("Inbox",					"Inv_SysOpen",			"Inv_SysClosed",		FALSE));
 		 
 	addEntry(LLFolderType::FT_NONE, 				new ViewerFolderEntry("New Folder",				"Inv_FolderOpen",		"Inv_FolderClosed",		FALSE, "default"));
diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp
index e59e685f53adc4e310abdd752f0da64e0bcac325..77c8bb032949d7bd9b9f0bd94252b0e2e0ba3f8a 100644
--- a/indra/newview/llviewerjointmesh.cpp
+++ b/indra/newview/llviewerjointmesh.cpp
@@ -55,6 +55,7 @@
 #include "v4math.h"
 #include "m3math.h"
 #include "m4math.h"
+#include "llmatrix4a.h"
 
 #if !LL_DARWIN && !LL_LINUX && !LL_SOLARIS
 extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB;
@@ -375,6 +376,7 @@ const S32 NUM_AXES = 3;
 // pivot parent 0-n -- child = n+1
 
 static LLMatrix4	gJointMatUnaligned[32];
+static LLMatrix4a	gJointMatAligned[32];
 static LLMatrix3	gJointRotUnaligned[32];
 static LLVector4	gJointPivot[32];
 
@@ -460,6 +462,14 @@ void LLViewerJointMesh::uploadJointMatrices()
 		glUniform4fvARB(gAvatarMatrixParam, 45, mat);
 		stop_glerror();
 	}
+	else
+	{
+		//load gJointMatUnaligned into gJointMatAligned
+		for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); ++joint_num)
+		{
+			gJointMatAligned[joint_num].loadu(gJointMatUnaligned[joint_num]);
+		}
+	}
 }
 
 //--------------------------------------------------------------------
@@ -501,7 +511,7 @@ int compare_int(const void *a, const void *b)
 U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 {
 	if (!mValid || !mMesh || !mFace || !mVisible || 
-		mFace->mVertexBuffer.isNull() ||
+		!mFace->getVertexBuffer() ||
 		mMesh->getNumFaces() == 0) 
 	{
 		return 0;
@@ -509,6 +519,8 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 
 	U32 triangle_count = 0;
 
+	S32 diffuse_channel = LLDrawPoolAvatar::sDiffuseChannel;
+
 	stop_glerror();
 	
 	//----------------------------------------------------------------
@@ -521,7 +533,7 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 
 	stop_glerror();
 	
-	LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0));
+	LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), mFace->getPool()->getVertexShaderLevel() > 0 ? 0.f : mShiny);
 
 	//----------------------------------------------------------------
 	// setup current texture
@@ -531,7 +543,7 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 	LLTexUnit::eTextureAddressMode old_mode = LLTexUnit::TAM_WRAP;
 	if (mTestImageName)
 	{
-		gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mTestImageName);
+		gGL.getTexUnit(diffuse_channel)->bindManual(LLTexUnit::TT_TEXTURE, mTestImageName);
 
 		if (mIsTransparent)
 		{
@@ -540,24 +552,18 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 		else
 		{
 			glColor4f(0.7f, 0.6f, 0.3f, 1.f);
-			gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_LERP_TEX_ALPHA, LLTexUnit::TBS_TEX_COLOR, LLTexUnit::TBS_PREV_COLOR);
+			gGL.getTexUnit(diffuse_channel)->setTextureColorBlend(LLTexUnit::TBO_LERP_TEX_ALPHA, LLTexUnit::TBS_TEX_COLOR, LLTexUnit::TBS_PREV_COLOR);
 		}
 	}
 	else if( !is_dummy && mLayerSet )
 	{
 		if(	mLayerSet->hasComposite() )
 		{
-			gGL.getTexUnit(0)->bind(mLayerSet->getComposite());
+			gGL.getTexUnit(diffuse_channel)->bind(mLayerSet->getComposite());
 		}
 		else
 		{
-			// This warning will always trigger if you've hacked the avatar to show as incomplete.
-			// Ignore the warning if that's the case.
-			if (!gSavedSettings.getBOOL("RenderUnloadedAvatar"))
-			{
-				//llwarns << "Layerset without composite" << llendl;
-			}
-			gGL.getTexUnit(0)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT));
+			gGL.getTexUnit(diffuse_channel)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT));
 		}
 	}
 	else
@@ -567,16 +573,16 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 		{
 			old_mode = mTexture->getAddressMode();
 		}
-		gGL.getTexUnit(0)->bind(mTexture.get());
-		gGL.getTexUnit(0)->bind(mTexture);
-		gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+		gGL.getTexUnit(diffuse_channel)->bind(mTexture.get());
+		gGL.getTexUnit(diffuse_channel)->bind(mTexture);
+		gGL.getTexUnit(diffuse_channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
 	}
 	else
 	{
-		gGL.getTexUnit(0)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT));
+		gGL.getTexUnit(diffuse_channel)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT));
 	}
 	
-	mFace->mVertexBuffer->setBuffer(sRenderMask);
+	mFace->getVertexBuffer()->setBuffer(sRenderMask);
 
 	U32 start = mMesh->mFaceVertexOffset;
 	U32 end = start + mMesh->mFaceVertexCount - 1;
@@ -593,14 +599,14 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 			}
 		}
 		
-		mFace->mVertexBuffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+		mFace->getVertexBuffer()->drawRange(LLRender::TRIANGLES, start, end, count, offset);
 	}
 	else
 	{
 		glPushMatrix();
 		LLMatrix4 jointToWorld = getWorldMatrix();
 		glMultMatrixf((GLfloat*)jointToWorld.mMatrix);
-		mFace->mVertexBuffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+		mFace->getVertexBuffer()->drawRange(LLRender::TRIANGLES, start, end, count, offset);
 		glPopMatrix();
 	}
 	gPipeline.addTrianglesDrawn(count);
@@ -609,13 +615,13 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 	
 	if (mTestImageName)
 	{
-		gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
+		gGL.getTexUnit(diffuse_channel)->setTextureBlendType(LLTexUnit::TB_MULT);
 	}
 
 	if (mTexture.notNull() && !is_dummy)
 	{
-		gGL.getTexUnit(0)->bind(mTexture);
-		gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
+		gGL.getTexUnit(diffuse_channel)->bind(mTexture);
+		gGL.getTexUnit(diffuse_channel)->setTextureAddressMode(old_mode);
 	}
 
 	return triangle_count;
@@ -626,6 +632,9 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 //-----------------------------------------------------------------------------
 void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area)
 {
+	//bump num_vertices to next multiple of 4
+	num_vertices = (num_vertices + 0x3) & ~0x3;
+
 	// Do a pre-alloc pass to determine sizes of data.
 	if (mMesh && mValid)
 	{
@@ -648,13 +657,25 @@ static LLFastTimer::DeclareTimer FTM_AVATAR_FACE("Avatar Face");
 
 void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind, bool terse_update)
 {
+	//IF THIS FUNCTION BREAKS, SEE LLPOLYMESH CONSTRUCTOR AND CHECK ALIGNMENT OF INPUT ARRAYS
+
 	mFace = face;
 
-	if (mFace->mVertexBuffer.isNull())
+	if (!mFace->getVertexBuffer())
 	{
 		return;
 	}
 
+	LLDrawPool *poolp = mFace->getPool();
+	BOOL hardware_skinning = (poolp && poolp->getVertexShaderLevel() > 0) ? TRUE : FALSE;
+
+	if (!hardware_skinning && terse_update)
+	{ //no need to do terse updates if we're doing software vertex skinning
+	 // since mMesh is being copied into mVertexBuffer every frame
+		return;
+	}
+
+
 	LLFastTimer t(FTM_AVATAR_FACE);
 
 	LLStrider<LLVector3> verticesp;
@@ -667,111 +688,59 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_w
 	// Copy data into the faces from the polymesh data.
 	if (mMesh && mValid)
 	{
-		if (mMesh->getNumVertices())
+		const U32 num_verts = mMesh->getNumVertices();
+
+		if (num_verts)
 		{
-			stop_glerror();
 			face->getGeometryAvatar(verticesp, normalsp, tex_coordsp, vertex_weightsp, clothing_weightsp);
-			stop_glerror();
-			face->mVertexBuffer->getIndexStrider(indicesp);
-			stop_glerror();
+			face->getVertexBuffer()->getIndexStrider(indicesp);
 
 			verticesp += mMesh->mFaceVertexOffset;
-			tex_coordsp += mMesh->mFaceVertexOffset;
 			normalsp += mMesh->mFaceVertexOffset;
-			vertex_weightsp += mMesh->mFaceVertexOffset;
-			clothing_weightsp += mMesh->mFaceVertexOffset;
-
-			const U32* __restrict coords = (U32*) mMesh->getCoords();
-			const U32* __restrict tex_coords = (U32*) mMesh->getTexCoords();
-			const U32* __restrict normals = (U32*) mMesh->getNormals();
-			const U32* __restrict weights = (U32*) mMesh->getWeights();
-			const U32* __restrict cloth_weights = (U32*) mMesh->getClothingWeights();
-
-			const U32 num_verts = mMesh->getNumVertices();
-
-			U32 i = 0;
-
-			const U32 skip = verticesp.getSkip()/sizeof(U32);
+			
+			F32* v = (F32*) verticesp.get();
+			F32* n = (F32*) normalsp.get();
+			
+			U32 words = num_verts*4;
 
-			U32* __restrict v = (U32*) verticesp.get();
-			U32* __restrict n = (U32*) normalsp.get();
+			LLVector4a::memcpyNonAliased16(v, (F32*) mMesh->getCoords(), words*sizeof(F32));
+			LLVector4a::memcpyNonAliased16(n, (F32*) mMesh->getNormals(), words*sizeof(F32));
+						
 			
-			if (terse_update)
+			if (!terse_update)
 			{
-				for (S32 i = num_verts; i > 0; --i)
-				{
-					//morph target application only, only update positions and normals
-					v[0] = coords[0]; 
-					v[1] = coords[1]; 
-					v[2] = coords[2];		
-					coords += 3;
-					v += skip;
-				}
+				vertex_weightsp += mMesh->mFaceVertexOffset;
+				clothing_weightsp += mMesh->mFaceVertexOffset;
+				tex_coordsp += mMesh->mFaceVertexOffset;
+		
+				F32* tc = (F32*) tex_coordsp.get();
+				F32* vw = (F32*) vertex_weightsp.get();
+				F32* cw = (F32*) clothing_weightsp.get();	
 
-				for (S32 i = num_verts; i > 0; --i)
-				{
-					n[0] = normals[0]; 
-					n[1] = normals[1];
-					n[2] = normals[2];
-					normals += 3;
-					n += skip;
-				}
+				LLVector4a::memcpyNonAliased16(tc, (F32*) mMesh->getTexCoords(), num_verts*2*sizeof(F32));
+				LLVector4a::memcpyNonAliased16(vw, (F32*) mMesh->getWeights(), num_verts*sizeof(F32));	
+				LLVector4a::memcpyNonAliased16(cw, (F32*) mMesh->getClothingWeights(), num_verts*4*sizeof(F32));	
 			}
-			else
-				{
 
-				U32* __restrict tc = (U32*) tex_coordsp.get();
-				U32* __restrict vw = (U32*) vertex_weightsp.get();
-				U32* __restrict cw = (U32*) clothing_weightsp.get();
-				
-				do
-				{
-					v[0] = *(coords++); 
-					v[1] = *(coords++); 
-					v[2] = *(coords++);
-					v += skip;
-
-					tc[0] = *(tex_coords++); 
-					tc[1] = *(tex_coords++);
-					tc += skip;
-
-					n[0] = *(normals++); 
-					n[1] = *(normals++);
-					n[2] = *(normals++);
-					n += skip;
-
-					vw[0] = *(weights++);
-					vw += skip;
-
-					cw[0] = *(cloth_weights++);
-					cw[1] = *(cloth_weights++);
-					cw[2] = *(cloth_weights++);
-					cw[3] = *(cloth_weights++);
-					cw += skip;
-				}
-				while (++i < num_verts);
+			const U32 idx_count = mMesh->getNumFaces()*3;
 
-				const U32 idx_count = mMesh->getNumFaces()*3;
+			indicesp += mMesh->mFaceIndexOffset;
 
-				indicesp += mMesh->mFaceIndexOffset;
+			U16* __restrict idx = indicesp.get();
+			S32* __restrict src_idx = (S32*) mMesh->getFaces();	
 
-				U16* __restrict idx = indicesp.get();
-				S32* __restrict src_idx = (S32*) mMesh->getFaces();
+			const S32 offset = (S32) mMesh->mFaceVertexOffset;
 
-				i = 0;
-
-				const S32 offset = (S32) mMesh->mFaceVertexOffset;
-
-				do
-				{
-					*(idx++) = *(src_idx++)+offset;
-				}
-				while (++i < idx_count);
+			for (S32 i = 0; i < idx_count; ++i)
+			{
+				*(idx++) = *(src_idx++)+offset;
 			}
 		}
 	}
 }
 
+
+
 //-----------------------------------------------------------------------------
 // updateLOD()
 //-----------------------------------------------------------------------------
@@ -789,89 +758,49 @@ void LLViewerJointMesh::updateGeometryOriginal(LLFace *mFace, LLPolyMesh *mMesh)
 	LLStrider<LLVector3> o_normals;
 
 	//get vertex and normal striders
-	LLVertexBuffer *buffer = mFace->mVertexBuffer;
+	LLVertexBuffer* buffer = mFace->getVertexBuffer();
 	buffer->getVertexStrider(o_vertices,  0);
 	buffer->getNormalStrider(o_normals,   0);
 
-	F32 last_weight = F32_MAX;
-	LLMatrix4 gBlendMat;
-	LLMatrix3 gBlendRotMat;
+	F32* __restrict vert = o_vertices[0].mV;
+	F32* __restrict norm = o_normals[0].mV;
+
+	const F32* __restrict weights = mMesh->getWeights();
+	const LLVector4a* __restrict coords = (LLVector4a*) mMesh->getCoords();
+	const LLVector4a* __restrict normals = (LLVector4a*) mMesh->getNormals();
+
+	U32 offset = mMesh->mFaceVertexOffset*4;
+	vert += offset;
+	norm += offset;
 
-	const F32* weights = mMesh->getWeights();
-	const LLVector3* coords = mMesh->getCoords();
-	const LLVector3* normals = mMesh->getNormals();
 	for (U32 index = 0; index < mMesh->getNumVertices(); index++)
 	{
-		U32 bidx = index + mMesh->mFaceVertexOffset;
-		
-		// blend by first matrix
-		F32 w = weights[index]; 
-		
-		// Maybe we don't have to change gBlendMat.
-		// Profiles of a single-avatar scene on a Mac show this to be a very
-		// common case.  JC
-		if (w == last_weight)
-		{
-			o_vertices[bidx] = coords[index] * gBlendMat;
-			o_normals[bidx] = normals[index] * gBlendRotMat;
-			continue;
-		}
-		
-		last_weight = w;
+		// equivalent to joint = floorf(weights[index]);
+		S32 joint = _mm_cvtt_ss2si(_mm_load_ss(weights+index));
+		F32 w = weights[index] - joint;		
 
-		S32 joint = llfloor(w);
-		w -= joint;
-		
-		// No lerp required in this case.
-		if (w == 1.0f)
+		LLMatrix4a gBlendMat;
+
+		if (w != 0.f)
 		{
-			gBlendMat = gJointMatUnaligned[joint+1];
-			o_vertices[bidx] = coords[index] * gBlendMat;
-			gBlendRotMat = gJointRotUnaligned[joint+1];
-			o_normals[bidx] = normals[index] * gBlendRotMat;
-			continue;
+			// blend between matrices and apply
+			gBlendMat.setLerp(gJointMatAligned[joint+0],
+							  gJointMatAligned[joint+1], w);
+
+			LLVector4a res;
+			gBlendMat.affineTransform(coords[index], res);
+			res.store4a(vert+index*4);
+			gBlendMat.rotate(normals[index], res);
+			res.store4a(norm+index*4);
+		}
+		else
+		{  // No lerp required in this case.
+			LLVector4a res;
+			gJointMatAligned[joint].affineTransform(coords[index], res);
+			res.store4a(vert+index*4);
+			gJointMatAligned[joint].rotate(normals[index], res);
+			res.store4a(norm+index*4);
 		}
-		
-		// Try to keep all the accesses to the matrix data as close
-		// together as possible.  This function is a hot spot on the
-		// Mac. JC
-		LLMatrix4 &m0 = gJointMatUnaligned[joint+1];
-		LLMatrix4 &m1 = gJointMatUnaligned[joint+0];
-		
-		gBlendMat.mMatrix[VX][VX] = lerp(m1.mMatrix[VX][VX], m0.mMatrix[VX][VX], w);
-		gBlendMat.mMatrix[VX][VY] = lerp(m1.mMatrix[VX][VY], m0.mMatrix[VX][VY], w);
-		gBlendMat.mMatrix[VX][VZ] = lerp(m1.mMatrix[VX][VZ], m0.mMatrix[VX][VZ], w);
-
-		gBlendMat.mMatrix[VY][VX] = lerp(m1.mMatrix[VY][VX], m0.mMatrix[VY][VX], w);
-		gBlendMat.mMatrix[VY][VY] = lerp(m1.mMatrix[VY][VY], m0.mMatrix[VY][VY], w);
-		gBlendMat.mMatrix[VY][VZ] = lerp(m1.mMatrix[VY][VZ], m0.mMatrix[VY][VZ], w);
-
-		gBlendMat.mMatrix[VZ][VX] = lerp(m1.mMatrix[VZ][VX], m0.mMatrix[VZ][VX], w);
-		gBlendMat.mMatrix[VZ][VY] = lerp(m1.mMatrix[VZ][VY], m0.mMatrix[VZ][VY], w);
-		gBlendMat.mMatrix[VZ][VZ] = lerp(m1.mMatrix[VZ][VZ], m0.mMatrix[VZ][VZ], w);
-
-		gBlendMat.mMatrix[VW][VX] = lerp(m1.mMatrix[VW][VX], m0.mMatrix[VW][VX], w);
-		gBlendMat.mMatrix[VW][VY] = lerp(m1.mMatrix[VW][VY], m0.mMatrix[VW][VY], w);
-		gBlendMat.mMatrix[VW][VZ] = lerp(m1.mMatrix[VW][VZ], m0.mMatrix[VW][VZ], w);
-
-		o_vertices[bidx] = coords[index] * gBlendMat;
-		
-		LLMatrix3 &n0 = gJointRotUnaligned[joint+1];
-		LLMatrix3 &n1 = gJointRotUnaligned[joint+0];
-		
-		gBlendRotMat.mMatrix[VX][VX] = lerp(n1.mMatrix[VX][VX], n0.mMatrix[VX][VX], w);
-		gBlendRotMat.mMatrix[VX][VY] = lerp(n1.mMatrix[VX][VY], n0.mMatrix[VX][VY], w);
-		gBlendRotMat.mMatrix[VX][VZ] = lerp(n1.mMatrix[VX][VZ], n0.mMatrix[VX][VZ], w);
-
-		gBlendRotMat.mMatrix[VY][VX] = lerp(n1.mMatrix[VY][VX], n0.mMatrix[VY][VX], w);
-		gBlendRotMat.mMatrix[VY][VY] = lerp(n1.mMatrix[VY][VY], n0.mMatrix[VY][VY], w);
-		gBlendRotMat.mMatrix[VY][VZ] = lerp(n1.mMatrix[VY][VZ], n0.mMatrix[VY][VZ], w);
-
-		gBlendRotMat.mMatrix[VZ][VX] = lerp(n1.mMatrix[VZ][VX], n0.mMatrix[VZ][VX], w);
-		gBlendRotMat.mMatrix[VZ][VY] = lerp(n1.mMatrix[VZ][VY], n0.mMatrix[VZ][VY], w);
-		gBlendRotMat.mMatrix[VZ][VZ] = lerp(n1.mMatrix[VZ][VZ], n0.mMatrix[VZ][VZ], w);
-		
-		o_normals[bidx] = normals[index] * gBlendRotMat;
 	}
 
 	buffer->setBuffer(0);
@@ -940,7 +869,7 @@ void LLViewerJointMesh::updateJointGeometry()
 		  && mMesh
 		  && mFace
 		  && mMesh->hasWeights()
-		  && mFace->mVertexBuffer.notNull()
+		  && mFace->getVertexBuffer()
 		  && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) == 0))
 	{
 		return;
diff --git a/indra/newview/llviewerjointmesh_sse.cpp b/indra/newview/llviewerjointmesh_sse.cpp
index 174b6eae29e15e3dd2900873740b8446b74aafd5..400b49d04602a58cba668a69362d5dc3cb9c1da1 100644
--- a/indra/newview/llviewerjointmesh_sse.cpp
+++ b/indra/newview/llviewerjointmesh_sse.cpp
@@ -83,13 +83,13 @@ void LLViewerJointMesh::updateGeometrySSE(LLFace *face, LLPolyMesh *mesh)
 	LLStrider<LLVector3> o_vertices;
 	LLStrider<LLVector3> o_normals;
 
-	LLVertexBuffer *buffer = face->mVertexBuffer;
+	LLVertexBuffer *buffer = face->getVertexBuffer();
 	buffer->getVertexStrider(o_vertices,  mesh->mFaceVertexOffset);
 	buffer->getNormalStrider(o_normals,   mesh->mFaceVertexOffset);
 
 	const F32*			weights			= mesh->getWeights();
-	const LLVector3*	coords			= mesh->getCoords();
-	const LLVector3*	normals			= mesh->getNormals();
+	const LLVector3*	coords			= (const LLVector3*)mesh->getCoords();
+	const LLVector3*	normals			= (const LLVector3*)mesh->getNormals();
 	for (U32 index = 0, index_end = mesh->getNumVertices(); index < index_end; ++index)
 	{
 		if( weight != weights[index])
diff --git a/indra/newview/llviewerjointmesh_sse2.cpp b/indra/newview/llviewerjointmesh_sse2.cpp
index a374ba247165e5e0cc65c06a7b4fd50ba07b8a13..c2296dd56964dd52ac06e3035c6c552a9969a0f7 100644
--- a/indra/newview/llviewerjointmesh_sse2.cpp
+++ b/indra/newview/llviewerjointmesh_sse2.cpp
@@ -90,13 +90,13 @@ void LLViewerJointMesh::updateGeometrySSE2(LLFace *face, LLPolyMesh *mesh)
 	LLStrider<LLVector3> o_vertices;
 	LLStrider<LLVector3> o_normals;
 
-	LLVertexBuffer *buffer = face->mVertexBuffer;
+	LLVertexBuffer *buffer = face->getVertexBuffer();
 	buffer->getVertexStrider(o_vertices,  mesh->mFaceVertexOffset);
 	buffer->getNormalStrider(o_normals,   mesh->mFaceVertexOffset);
 
 	const F32*			weights			= mesh->getWeights();
-	const LLVector3*	coords			= mesh->getCoords();
-	const LLVector3*	normals			= mesh->getNormals();
+	const LLVector3*	coords			= (const LLVector3*)mesh->getCoords();
+	const LLVector3*	normals			= (const LLVector3*)mesh->getNormals();
 	for (U32 index = 0, index_end = mesh->getNumVertices(); index < index_end; ++index)
 	{
 		if( weight != weights[index])
diff --git a/indra/newview/llviewerjointmesh_vec.cpp b/indra/newview/llviewerjointmesh_vec.cpp
index c9371030ad9a997d83f9d3210d151a69556da93a..6600d01d171072cc52b5b8b626cae8f2d35f2036 100644
--- a/indra/newview/llviewerjointmesh_vec.cpp
+++ b/indra/newview/llviewerjointmesh_vec.cpp
@@ -46,6 +46,7 @@
 // static
 void LLViewerJointMesh::updateGeometryVectorized(LLFace *face, LLPolyMesh *mesh)
 {
+#if 0
 	static LLV4Matrix4	sJointMat[32];
 	LLDynamicArray<LLJointRenderData*>& joint_data = mesh->getReferenceMesh()->mJointRenderData;
 	S32 j, joint_num, joint_end = joint_data.count();
@@ -92,4 +93,5 @@ void LLViewerJointMesh::updateGeometryVectorized(LLFace *face, LLPolyMesh *mesh)
 	}
 
 	buffer->setBuffer(0);
+#endif
 }
diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp
index e1dd72dddcc3acefc2fc8cac4bd3dba0f54ccd46..fbf11f20dbb956213c5f8d72a0766106b70df2e7 100644
--- a/indra/newview/llviewerjoystick.cpp
+++ b/indra/newview/llviewerjoystick.cpp
@@ -758,7 +758,7 @@ void LLViewerJoystick::moveAvatar(bool reset)
 	sDelta[RX_I] += (cur_delta[RX_I] - sDelta[RX_I]) * time * feather;
 	sDelta[RY_I] += (cur_delta[RY_I] - sDelta[RY_I]) * time * feather;
 	
-	handleRun(fsqrtf(sDelta[Z_I]*sDelta[Z_I] + sDelta[X_I]*sDelta[X_I]));
+	handleRun((F32) sqrt(sDelta[Z_I]*sDelta[Z_I] + sDelta[X_I]*sDelta[X_I]));
 	
 	// Allow forward/backward movement some priority
 	if (dom_axis == Z_I)
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index bc326540e695f01a76e645d1e4f1ee6c36fbe518..79c6c8db75b581b8764acf2450a9153181a86ddf 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -914,7 +914,7 @@ void LLViewerMedia::updateMedia(void *dummy_arg)
 				
 				// Set the low priority size for downsampling to approximately the size the texture is displayed at.
 				{
-					F32 approximate_interest_dimension = fsqrtf(pimpl->getInterest());
+					F32 approximate_interest_dimension = (F32) sqrt(pimpl->getInterest());
 					
 					pimpl->setLowPrioritySizeLimit(llround(approximate_interest_dimension));
 				}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index b4ec3c135b73dad5f09ee62b8c32a43ee9bafbf0..8cfe91203a5c8e40f3002d33c34cefe34bf7e8fc 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -77,6 +77,7 @@
 #include "llmoveview.h"
 #include "llparcel.h"
 #include "llrootview.h"
+#include "llsceneview.h"
 #include "llselectmgr.h"
 #include "llsidetray.h"
 #include "llstatusbar.h"
@@ -168,7 +169,6 @@ LLMenuItemCallGL* gBusyMenu = NULL;
 // Local prototypes
 
 // File Menu
-const char* upload_pick(void* data);
 void handle_compress_image(void*);
 
 
@@ -455,7 +455,7 @@ void init_menus()
 	gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", upload_cost);
 	gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", upload_cost);
 	gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", upload_cost);
-
+	
 	gAFKMenu = gMenuBarView->getChild<LLMenuItemCallGL>("Set Away", TRUE);
 	gBusyMenu = gMenuBarView->getChild<LLMenuItemCallGL>("Set Busy", TRUE);
 	gAttachSubMenu = gMenuBarView->findChildMenuByName("Attach Object", TRUE);
@@ -517,6 +517,11 @@ class LLAdvancedToggleConsole : public view_listener_t
 		{
 			toggle_visibility( (void*)gDebugView->mFastTimerView );
 		}
+		else if ("scene view" == console_type)
+		{
+			toggle_visibility( (void*)gSceneView);
+		}
+
 #if MEM_TRACK_MEM
 		else if ("memory view" == console_type)
 		{
@@ -552,6 +557,10 @@ class LLAdvancedCheckConsole : public view_listener_t
 		{
 			new_value = get_visibility( (void*)gDebugView->mFastTimerView );
 		}
+		else if ("scene view" == console_type)
+		{
+			new_value = get_visibility( (void*) gSceneView);
+		}
 #if MEM_TRACK_MEM
 		else if ("memory view" == console_type)
 		{
@@ -699,7 +708,7 @@ U32 render_type_from_string(std::string render_type)
 	{
 		return LLPipeline::RENDER_TYPE_AVATAR;
 	}
-	else if ("surfacePath" == render_type)
+	else if ("surfacePatch" == render_type)
 	{
 		return LLPipeline::RENDER_TYPE_TERRAIN;
 	}
@@ -903,6 +912,10 @@ U32 info_display_from_string(std::string info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_BBOXES;
 	}
+	else if ("normals" == info_display)
+	{
+		return LLPipeline::RENDER_DEBUG_NORMALS;
+	}
 	else if ("points" == info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_POINTS;
@@ -915,6 +928,10 @@ U32 info_display_from_string(std::string info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA;
 	}
+	else if ("physics shapes" == info_display)
+	{
+		return LLPipeline::RENDER_DEBUG_PHYSICS_SHAPES;
+	}
 	else if ("occlusion" == info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_OCCLUSION;
@@ -947,6 +964,14 @@ U32 info_display_from_string(std::string info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_FACE_AREA;
 	}
+	else if ("lod info" == info_display)
+	{
+		return LLPipeline::RENDER_DEBUG_LOD_INFO;
+	}
+	else if ("build queue" == info_display)
+	{
+		return LLPipeline::RENDER_DEBUG_BUILD_QUEUE;
+	}
 	else if ("lights" == info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_LIGHTS;
@@ -1907,19 +1932,20 @@ class LLAdvancedAgentPilot : public view_listener_t
 		std::string command = userdata.asString();
 		if ("start playback" == command)
 		{
-			LLAgentPilot::startPlayback(NULL);
+			gAgentPilot.setNumRuns(-1);
+			gAgentPilot.startPlayback();
 		}
 		else if ("stop playback" == command)
 		{
-			LLAgentPilot::stopPlayback(NULL);
+			gAgentPilot.stopPlayback();
 		}
 		else if ("start record" == command)
 		{
-			LLAgentPilot::startRecord(NULL);
+			gAgentPilot.startRecord();
 		}
 		else if ("stop record" == command)
 		{
-			LLAgentPilot::saveRecord(NULL);
+			gAgentPilot.stopRecord();
 		}
 
 		return true;
@@ -1937,7 +1963,7 @@ class LLAdvancedToggleAgentPilotLoop : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
-		LLAgentPilot::sLoop = !(LLAgentPilot::sLoop);
+		gAgentPilot.setLoop(!gAgentPilot.getLoop());
 		return true;
 	}
 };
@@ -1946,7 +1972,7 @@ class LLAdvancedCheckAgentPilotLoop : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
-		bool new_value = LLAgentPilot::sLoop;
+		bool new_value = gAgentPilot.getLoop();
 		return new_value;
 	}
 };
@@ -2074,7 +2100,7 @@ class LLAdvancedEnableRenderDeferred: public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
-		bool new_value = gSavedSettings.getBOOL("RenderUseFBO") && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT > 0) &&
+		bool new_value = gGLManager.mHasFramebufferObject && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1 &&
 			LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0;
 		return new_value;
 	}
@@ -2087,7 +2113,8 @@ class LLAdvancedEnableRenderDeferredOptions: public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
-		bool new_value = gSavedSettings.getBOOL("RenderUseFBO") && gSavedSettings.getBOOL("RenderDeferred");
+		bool new_value = gGLManager.mHasFramebufferObject && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1 &&
+			LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0 && gSavedSettings.getBOOL("RenderDeferred");
 		return new_value;
 	}
 };
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 2cf8dbec89ec798e1a637eb8da2504aa78904def..37640ad0d45ea7cd4a62de1b5ecd2b29e3e394e4 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -34,6 +34,7 @@
 #include "llfilepicker.h"
 #include "llfloaterreg.h"
 #include "llbuycurrencyhtml.h"
+#include "llfloatermodelpreview.h"
 #include "llfloatersnapshot.h"
 #include "llimage.h"
 #include "llimagebmp.h"
@@ -52,13 +53,14 @@
 #include "llvfs.h"
 #include "llviewerinventory.h"
 #include "llviewermenu.h"	// gMenuHolder
+#include "llviewerparcelmgr.h"
 #include "llviewerregion.h"
 #include "llviewerstats.h"
 #include "llviewerwindow.h"
 #include "llappviewer.h"
 #include "lluploaddialog.h"
 #include "lltrans.h"
-
+#include "llfloaterbuycurrency.h"
 
 // linden libraries
 #include "llassetuploadresponders.h"
@@ -66,6 +68,7 @@
 #include "llhttpclient.h"
 #include "llnotificationsutil.h"
 #include "llsdserialize.h"
+#include "llsdutil.h"
 #include "llstring.h"
 #include "lltransactiontypes.h"
 #include "lluuid.h"
@@ -84,6 +87,99 @@ class LLFileEnableUpload : public view_listener_t
 	}
 };
 
+class LLFileEnableUploadModel : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		return true;
+	}
+};
+
+class LLMeshEnabled : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		return gSavedSettings.getBOOL("MeshEnabled");
+	}
+};
+
+class LLMeshUploadVisible : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		return gSavedSettings.getBOOL("MeshEnabled") && 
+			   LLViewerParcelMgr::getInstance()->allowAgentBuild() && 
+			   !gAgent.getRegion()->getCapability("ObjectAdd").empty();
+	}
+};
+
+LLMutex* LLFilePickerThread::sMutex = NULL;
+std::queue<LLFilePickerThread*> LLFilePickerThread::sDeadQ;
+
+void LLFilePickerThread::getFile()
+{
+#if LL_WINDOWS
+	start();
+#else
+	run();
+#endif
+}
+
+//virtual 
+void LLFilePickerThread::run()
+{
+	LLFilePicker picker;
+#if LL_WINDOWS
+	if (picker.getOpenFile(mFilter, false))
+	{
+		mFile = picker.getFirstFile();
+	}
+#else
+	if (picker.getOpenFile(mFilter, true))
+	{
+		mFile = picker.getFirstFile();
+	}
+#endif
+
+	{
+		LLMutexLock lock(sMutex);
+		sDeadQ.push(this);
+	}
+
+}
+
+//static
+void LLFilePickerThread::initClass()
+{
+	sMutex = new LLMutex(NULL);
+}
+
+//static
+void LLFilePickerThread::cleanupClass()
+{
+	clearDead();
+	
+	delete sMutex;
+	sMutex = NULL;
+}
+
+//static
+void LLFilePickerThread::clearDead()
+{
+	if (!sDeadQ.empty())
+	{
+		LLMutexLock lock(sMutex);
+		while (!sDeadQ.empty())
+		{
+			LLFilePickerThread* thread = sDeadQ.front();
+			thread->notify(thread->mFile);
+			delete thread;
+			sDeadQ.pop();
+		}
+	}
+}
+
+
 //============================================================================
 
 #if LL_WINDOWS
@@ -97,6 +193,7 @@ static std::string XML_EXTENSIONS = "xml";
 static std::string SLOBJECT_EXTENSIONS = "slobject";
 #endif
 static std::string ALL_FILE_EXTENSIONS = "*.*";
+static std::string MODEL_EXTENSIONS = "dae";
 
 std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
 {
@@ -111,6 +208,8 @@ std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
 		return ANIM_EXTENSIONS;
 	case LLFilePicker::FFLOAD_SLOBJECT:
 		return SLOBJECT_EXTENSIONS;
+	case LLFilePicker::FFLOAD_MODEL:
+		return MODEL_EXTENSIONS;
 #ifdef _CORY_TESTING
 	case LLFilePicker::FFLOAD_GEOMETRY:
 		return GEOMETRY_EXTENSIONS;
@@ -254,6 +353,20 @@ class LLFileUploadImage : public view_listener_t
 	}
 };
 
+class LLFileUploadModel : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::getInstance("upload_model");
+		if (fmp)
+		{
+			fmp->loadModel(3);
+		}
+		
+		return TRUE;
+	}
+};
+	
 class LLFileUploadSound : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -315,10 +428,21 @@ class LLFileUploadBulk : public view_listener_t
 			LLAssetStorage::LLStoreAssetCallback callback = NULL;
 			S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
 			void *userdata = NULL;
-			upload_new_resource(filename, asset_name, asset_name, 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
-				LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(),
-					    display_name,
-					    callback, expected_upload_cost, userdata);
+
+			upload_new_resource(
+				filename,
+				asset_name,
+				asset_name,
+				0,
+				LLFolderType::FT_NONE,
+				LLInventoryType::IT_NONE,
+				LLFloaterPerms::getNextOwnerPerms(),
+				LLFloaterPerms::getGroupPerms(),
+				LLFloaterPerms::getEveryonePerms(),
+				display_name,
+				callback,
+				expected_upload_cost,
+				userdata);
 
 			// *NOTE: Ew, we don't iterate over the file list here,
 			// we handle the next files in upload_done_callback()
@@ -473,17 +597,20 @@ void handle_compress_image(void*)
 	}
 }
 
-void upload_new_resource(const std::string& src_filename, std::string name,
-			 std::string desc, S32 compression_info,
-			 LLFolderType::EType destination_folder_type,
-			 LLInventoryType::EType inv_type,
-			 U32 next_owner_perms,
-			 U32 group_perms,
-			 U32 everyone_perms,
-			 const std::string& display_name,
-			 LLAssetStorage::LLStoreAssetCallback callback,
-			 S32 expected_upload_cost,
-			 void *userdata)
+LLUUID upload_new_resource(
+	const std::string& src_filename,
+	std::string name,
+	std::string desc,
+	S32 compression_info,
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms,
+	const std::string& display_name,
+	LLAssetStorage::LLStoreAssetCallback callback,
+	S32 expected_upload_cost,
+	void *userdata)
 {	
 	// Generate the temporary UUID.
 	std::string filename = gDirUtilp->getTempFilename();
@@ -509,7 +636,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
 				short_name.c_str());
 		args["FILE"] = short_name;
  		upload_error(error_message, "NoFileExtension", filename, args);
-		return;
+		return LLUUID();
 	}
 	else if (codec != IMG_CODEC_INVALID)
 	{
@@ -522,7 +649,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
 			args["FILE"] = src_filename;
 			args["ERROR"] = LLImage::getLastError();
 			upload_error(error_message, "ProblemWithFile", filename, args);
-			return;
+			return LLUUID();
 		}
 	}
 	else if(exten == "wav")
@@ -550,7 +677,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
 					upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args);
 					break;	
 			}	
-			return;
+			return LLUUID();
 		}
 	}
 	else if(exten == "tmp")	 	
@@ -590,7 +717,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
                                                  error_message = llformat("corrupt resource file: %s", src_filename.c_str());
 												 args["FILE"] = src_filename;
 												 upload_error(error_message, "CorruptResourceFile", filename, args);
-                                                 return;
+                                                 return LLUUID();
                                          }	 	
 
                                          if (2 == tokens_read)	 	
@@ -618,7 +745,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
                                  error_message = llformat("unknown linden resource file version in file: %s", src_filename.c_str());
 								 args["FILE"] = src_filename;
 								 upload_error(error_message, "UnknownResourceFileVersion", filename, args);
-                                 return;
+                                 return LLUUID();
                          }	 	
                  }	 	
                  else	 	
@@ -660,7 +787,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
                          error_message = llformat( "Unable to create output file: %s", filename.c_str());
 						 args["FILE"] = filename;
 						 upload_error(error_message, "UnableToCreateOutputFile", filename, args);
-                         return;
+                         return LLUUID();
                  }	 	
 
                  fclose(in);	 	
@@ -674,7 +801,7 @@ void upload_new_resource(const std::string& src_filename, std::string name,
 	{
 		error_message = llformat("We do not currently support bulk upload of animation files\n");
 		upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args);
-		return;
+		return LLUUID();
 	}
 	else
 	{
@@ -720,9 +847,21 @@ void upload_new_resource(const std::string& src_filename, std::string name,
 		{
 			t_disp_name = src_filename;
 		}
-		upload_new_resource(tid, asset_type, name, desc, compression_info, // tid
-				    destination_folder_type, inv_type, next_owner_perms, group_perms, everyone_perms,
-				    display_name, callback, expected_upload_cost, userdata);
+		upload_new_resource(
+			tid,
+			asset_type,
+			name,
+			desc,
+			compression_info, // tid
+			destination_folder_type,
+			inv_type,
+			next_owner_perms,
+			group_perms,
+			everyone_perms,
+			display_name,
+			callback,
+			expected_upload_cost,
+			userdata);
 	}
 	else
 	{
@@ -736,9 +875,15 @@ void upload_new_resource(const std::string& src_filename, std::string name,
 		}
 		LLFilePicker::instance().reset();
 	}
+
+	return uuid;
 }
 
-void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
+void upload_done_callback(
+	const LLUUID& uuid,
+	void* user_data,
+	S32 result,
+	LLExtStat ext_status) // StoreAssetData callback (fixed)
 {
 	LLResourceData* data = (LLResourceData*)user_data;
 	S32 expected_upload_cost = data ? data->mExpectedUploadCost : 0;
@@ -843,35 +988,102 @@ void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result, LLExt
 		std::string display_name = LLStringUtil::null;
 		LLAssetStorage::LLStoreAssetCallback callback = NULL;
 		void *userdata = NULL;
-		upload_new_resource(next_file, asset_name, asset_name,	// file
-				    0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
-				    PERM_NONE, PERM_NONE, PERM_NONE,
-				    display_name,
-				    callback,
-				    expected_upload_cost, // assuming next in a group of uploads is of roughly the same type, i.e. same upload cost
-				    userdata);
+		upload_new_resource(
+			next_file,
+			asset_name,
+			asset_name,	// file
+			0,
+			LLFolderType::FT_NONE,
+			LLInventoryType::IT_NONE,
+			PERM_NONE,
+			PERM_NONE,
+			PERM_NONE,
+			display_name,
+			callback,
+			expected_upload_cost, // assuming next in a group of uploads is of roughly the same type, i.e. same upload cost
+			userdata);
 	}
 }
 
-void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type,
-			 std::string name,
-			 std::string desc, S32 compression_info,
-			 LLFolderType::EType destination_folder_type,
-			 LLInventoryType::EType inv_type,
-			 U32 next_owner_perms,
-			 U32 group_perms,
-			 U32 everyone_perms,
-			 const std::string& display_name,
-			 LLAssetStorage::LLStoreAssetCallback callback,
-			 S32 expected_upload_cost,
-			 void *userdata)
+static LLAssetID upload_new_resource_prep(
+	const LLTransactionID& tid,
+	LLAssetType::EType asset_type,
+	LLInventoryType::EType& inventory_type,
+	std::string& name,
+	const std::string& display_name,
+	std::string& description)
+{
+	LLAssetID uuid = generate_asset_id_for_new_upload(tid);
+
+	increase_new_upload_stats(asset_type);
+
+	assign_defaults_and_show_upload_message(
+		asset_type,
+		inventory_type,
+		name,
+		display_name,
+		description);
+
+	return uuid;
+}
+
+LLSD generate_new_resource_upload_capability_body(
+	LLAssetType::EType asset_type,
+	const std::string& name,
+	const std::string& desc,
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms)
+{
+	LLSD body;
+
+	body["folder_id"] = gInventory.findCategoryUUIDForType(
+		(destination_folder_type == LLFolderType::FT_NONE) ?
+		(LLFolderType::EType) asset_type :
+		destination_folder_type);
+
+	body["asset_type"] = LLAssetType::lookup(asset_type);
+	body["inventory_type"] = LLInventoryType::lookup(inv_type);
+	body["name"] = name;
+	body["description"] = desc;
+	body["next_owner_mask"] = LLSD::Integer(next_owner_perms);
+	body["group_mask"] = LLSD::Integer(group_perms);
+	body["everyone_mask"] = LLSD::Integer(everyone_perms);
+
+	return body;
+}
+
+void upload_new_resource(
+	const LLTransactionID &tid,
+	LLAssetType::EType asset_type,
+	std::string name,
+	std::string desc,
+	S32 compression_info,
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms,
+	const std::string& display_name,
+	LLAssetStorage::LLStoreAssetCallback callback,
+	S32 expected_upload_cost,
+	void *userdata)
 {
 	if(gDisconnected)
 	{
 		return ;
 	}
-
-	LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+	
+	LLAssetID uuid = 
+		upload_new_resource_prep(
+			tid,
+			asset_type,
+			inv_type,
+			name,
+			display_name,
+			desc);
 	
 	if( LLAssetType::AT_SOUND == asset_type )
 	{
@@ -916,26 +1128,32 @@ void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_ty
 	llinfos << "Expected Upload Cost: " << expected_upload_cost << llendl;
 	lldebugs << "Folder: " << gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type) << llendl;
 	lldebugs << "Asset Type: " << LLAssetType::lookup(asset_type) << llendl;
-	std::string url = gAgent.getRegion()->getCapability("NewFileAgentInventory");
-	if (!url.empty())
+
+	std::string url = gAgent.getRegion()->getCapability(
+		"NewFileAgentInventory");
+
+	if ( !url.empty() )
 	{
 		llinfos << "New Agent Inventory via capability" << llendl;
-		LLSD body;
-		body["folder_id"] = gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type);
-		body["asset_type"] = LLAssetType::lookup(asset_type);
-		body["inventory_type"] = LLInventoryType::lookup(inv_type);
-		body["name"] = name;
-		body["description"] = desc;
-		body["next_owner_mask"] = LLSD::Integer(next_owner_perms);
-		body["group_mask"] = LLSD::Integer(group_perms);
-		body["everyone_mask"] = LLSD::Integer(everyone_perms);
-		body["expected_upload_cost"] = LLSD::Integer(expected_upload_cost);
-		
-		//std::ostringstream llsdxml;
-		//LLSDSerialize::toPrettyXML(body, llsdxml);
-		//llinfos << "posting body to capability: " << llsdxml.str() << llendl;
 
-		LLHTTPClient::post(url, body, new LLNewAgentInventoryResponder(body, uuid, asset_type));
+		LLSD body;
+		body = generate_new_resource_upload_capability_body(
+			asset_type,
+			name,
+			desc,
+			destination_folder_type,
+			inv_type,
+			next_owner_perms,
+			group_perms,
+			everyone_perms);
+
+		LLHTTPClient::post(
+			url,
+			body,
+			new LLNewAgentInventoryResponder(
+				body,
+				uuid,
+				asset_type));
 	}
 	else
 	{
@@ -949,10 +1167,10 @@ void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_ty
 			S32 balance = gStatusBar->getBalance();
 			if (balance < expected_upload_cost)
 			{
+				// insufficient funds, bail on this upload
 				LLStringUtil::format_map_t args;
 				args["NAME"] = name;
 				args["AMOUNT"] = llformat("%d", expected_upload_cost);
-				// insufficient funds, bail on this upload
 				LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("UploadingCosts", args), expected_upload_cost );
 				return;
 			}
@@ -976,18 +1194,157 @@ void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_ty
 		{
 			asset_callback = callback;
 		}
-		gAssetStorage->storeAssetData(data->mAssetInfo.mTransactionID, data->mAssetInfo.mType,
-										asset_callback,
-										(void*)data,
-										FALSE);
+		gAssetStorage->storeAssetData(
+			data->mAssetInfo.mTransactionID,
+			data->mAssetInfo.mType,
+			asset_callback,
+			(void*)data,
+			FALSE);
+	}
+}
+
+BOOL upload_new_variable_price_resource(
+	const LLTransactionID &tid, 
+	LLAssetType::EType asset_type,
+	std::string name,
+	std::string desc, 
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms,
+	const std::string& display_name,
+	const LLSD& asset_resources)
+{
+	LLAssetID uuid = 
+		upload_new_resource_prep(
+			tid,
+			asset_type,
+			inv_type,
+			name,
+			display_name,
+			desc);
+
+	llinfos << "*** Uploading: " << llendl;
+	llinfos << "Type: " << LLAssetType::lookup(asset_type) << llendl;
+	llinfos << "UUID: " << uuid << llendl;
+	llinfos << "Name: " << name << llendl;
+	llinfos << "Desc: " << desc << llendl;
+	lldebugs << "Folder: "
+		<< gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? (LLFolderType::EType)asset_type : destination_folder_type) << llendl;
+	lldebugs << "Asset Type: " << LLAssetType::lookup(asset_type) << llendl;
+
+	std::string url = gAgent.getRegion()->getCapability(
+		"NewFileAgentInventoryVariablePrice");
+
+	if ( !url.empty() )
+	{
+		lldebugs
+			<< "New Agent Inventory variable price upload" << llendl;
+		
+		// Each of the two capabilities has similar data, so
+		// let's reuse that code
+
+		LLSD body;
+
+		body = generate_new_resource_upload_capability_body(
+			asset_type,
+			name,
+			desc,
+			destination_folder_type,
+			inv_type,
+			next_owner_perms,
+			group_perms,
+			everyone_perms);
+
+		body["asset_resources"] = asset_resources;
+
+		LLHTTPClient::post(
+			url,
+			body,
+			new LLNewAgentInventoryVariablePriceResponder(
+				uuid,
+				asset_type,
+				body));
+
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid)
+{
+	if ( gDisconnected )
+	{	
+		LLAssetID rv;
+
+		rv.setNull();
+		return rv;
+	}
+
+	LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+
+	return uuid;
+}
+
+void increase_new_upload_stats(LLAssetType::EType asset_type)
+{
+	if ( LLAssetType::AT_SOUND == asset_type )
+	{
+		LLViewerStats::getInstance()->incStat(
+			LLViewerStats::ST_UPLOAD_SOUND_COUNT );
+	}
+	else if ( LLAssetType::AT_TEXTURE == asset_type )
+	{
+		LLViewerStats::getInstance()->incStat(
+			LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
+	}
+	else if ( LLAssetType::AT_ANIMATION == asset_type )
+	{
+		LLViewerStats::getInstance()->incStat(
+			LLViewerStats::ST_UPLOAD_ANIM_COUNT );
+	}
+}
+
+void assign_defaults_and_show_upload_message(
+	LLAssetType::EType asset_type,
+	LLInventoryType::EType& inventory_type,
+	std::string& name,
+	const std::string& display_name,
+	std::string& description)
+{
+	if ( LLInventoryType::IT_NONE == inventory_type )
+	{
+		inventory_type = LLInventoryType::defaultForAssetType(asset_type);
+	}
+	LLStringUtil::stripNonprintable(name);
+	LLStringUtil::stripNonprintable(description);
+
+	if ( name.empty() )
+	{
+		name = "(No Name)";
+	}
+	if ( description.empty() )
+	{
+		description = "(No Description)";
 	}
+
+	// At this point, we're ready for the upload.
+	std::string upload_message = "Uploading...\n\n";
+	upload_message.append(display_name);
+	LLUploadDialog::modalUploadDialog(upload_message);
 }
 
+
 void init_menu_file()
 {
 	view_listener_t::addCommit(new LLFileUploadImage(), "File.UploadImage");
 	view_listener_t::addCommit(new LLFileUploadSound(), "File.UploadSound");
 	view_listener_t::addCommit(new LLFileUploadAnim(), "File.UploadAnim");
+	view_listener_t::addCommit(new LLFileUploadModel(), "File.UploadModel");
 	view_listener_t::addCommit(new LLFileUploadBulk(), "File.UploadBulk");
 	view_listener_t::addCommit(new LLFileCloseWindow(), "File.CloseWindow");
 	view_listener_t::addCommit(new LLFileCloseAllWindows(), "File.CloseAllWindows");
@@ -997,6 +1354,9 @@ void init_menu_file()
 	view_listener_t::addCommit(new LLFileQuit(), "File.Quit");
 
 	view_listener_t::addEnable(new LLFileEnableUpload(), "File.EnableUpload");
-	
+	view_listener_t::addEnable(new LLFileEnableUploadModel(), "File.EnableUploadModel");
+	view_listener_t::addMenu(new LLMeshEnabled(), "File.MeshEnabled");
+	view_listener_t::addMenu(new LLMeshUploadVisible(), "File.VisibleUploadModel");
+
 	// "File.SaveTexture" moved to llpanelmaininventory so that it can be properly handled.
 }
diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h
index 56b9e19049e68bbca31676dfccf12c0d16bb80f0..1597821504269e9d752e5621bed2765f2cbf0ab3 100644
--- a/indra/newview/llviewermenufile.h
+++ b/indra/newview/llviewermenufile.h
@@ -30,39 +30,118 @@
 #include "llfoldertype.h"
 #include "llassetstorage.h"
 #include "llinventorytype.h"
+#include "llfilepicker.h"
 
 class LLTransactionID;
 
 
 void init_menu_file();
 
-void upload_new_resource(const std::string& src_filename, 
-			 std::string name,
-			 std::string desc, 
-			 S32 compression_info,
-			 LLFolderType::EType destination_folder_type,
-			 LLInventoryType::EType inv_type,
-			 U32 next_owner_perms,
-			 U32 group_perms,
-			 U32 everyone_perms,
-			 const std::string& display_name,
-			 LLAssetStorage::LLStoreAssetCallback callback,
-			 S32 expected_upload_cost,
-			 void *userdata);
-
-void upload_new_resource(const LLTransactionID &tid, 
-			 LLAssetType::EType type,
-			 std::string name,
-			 std::string desc, 
-			 S32 compression_info,
-			 LLFolderType::EType destination_folder_type,
-			 LLInventoryType::EType inv_type,
-			 U32 next_owner_perms,
-			 U32 group_perms,
-			 U32 everyone_perms,
-			 const std::string& display_name,
-			 LLAssetStorage::LLStoreAssetCallback callback,
-			 S32 expected_upload_cost,
-			 void *userdata);
+LLUUID upload_new_resource(
+	const std::string& src_filename, 
+	std::string name,
+	std::string desc, 
+	S32 compression_info,
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms,
+	const std::string& display_name,
+	LLAssetStorage::LLStoreAssetCallback callback,
+	S32 expected_upload_cost,
+	void *userdata);
+
+void upload_new_resource(
+	const LLTransactionID &tid, 
+	LLAssetType::EType type,
+	std::string name,
+	std::string desc, 
+	S32 compression_info,
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms,
+	const std::string& display_name,
+	LLAssetStorage::LLStoreAssetCallback callback,
+	S32 expected_upload_cost,
+	void *userdata);
+
+// TODO* : Move all uploads to use this new function
+// since at some point, that upload path will be deprecated and no longer
+// used
+
+// We make a new function here to ensure that previous code is not broken
+BOOL upload_new_variable_price_resource(
+	const LLTransactionID& tid, 
+	LLAssetType::EType type,
+	std::string name,
+	std::string desc, 
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms,
+	const std::string& display_name,
+	const LLSD& asset_resources);
+
+LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid);
+void increase_new_upload_stats(LLAssetType::EType asset_type);
+void assign_defaults_and_show_upload_message(
+	LLAssetType::EType asset_type,
+	LLInventoryType::EType& inventory_type,
+	std::string& name,
+	const std::string& display_name,
+	std::string& description);
+
+LLSD generate_new_resource_upload_capability_body(
+	LLAssetType::EType asset_type,
+	const std::string& name,
+	const std::string& desc,
+	LLFolderType::EType destination_folder_type,
+	LLInventoryType::EType inv_type,
+	U32 next_owner_perms,
+	U32 group_perms,
+	U32 everyone_perms);
+
+void on_new_single_inventory_upload_complete(
+	LLAssetType::EType asset_type,
+	LLInventoryType::EType inventory_type,
+	const std::string inventory_type_string,
+	const LLUUID& item_folder_id,
+	const std::string& item_name,
+	const std::string& item_description,
+	const LLSD& server_response,
+	S32 upload_price);
+
+class LLFilePickerThread : public LLThread
+{ //multi-threaded file picker (runs system specific file picker in background and calls "notify" from main thread)
+public:
+
+	static std::queue<LLFilePickerThread*> sDeadQ;
+	static LLMutex* sMutex;
+
+	static void initClass();
+	static void cleanupClass();
+	static void clearDead();
+
+	std::string mFile; 
+
+	LLFilePicker::ELoadFilter mFilter;
+
+	LLFilePickerThread(LLFilePicker::ELoadFilter filter)
+		: LLThread("file picker"), mFilter(filter)
+	{
+
+	}
+
+	void getFile();
+
+	virtual void run();
+
+	virtual void notify(const std::string& filename) = 0;
+};
+
 
 #endif
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 298e789f65a4cfae4be05aec305acc1643c78b36..8b80e567b88394d639e7385df9d0ee6db2ca2ac4 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2802,7 +2802,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
 			{
 				LLVector3 pos, look_at;
 				U64 region_handle;
-				U8 region_access = SIM_ACCESS_MIN;
+				U8 region_access;
 				std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size);
 				std::string region_access_str = LLStringUtil::null;
 				std::string region_access_icn = LLStringUtil::null;
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index e8828e63a967cad904ede0c4b63789f66b15938c..6d493bfcd56f525843fb8a516635dcb11558e37c 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -47,6 +47,7 @@
 #include "llprimitive.h"
 #include "llquantize.h"
 #include "llregionhandle.h"
+#include "llsdserialize.h"
 #include "lltree_common.h"
 #include "llxfermanager.h"
 #include "message.h"
@@ -62,6 +63,7 @@
 #include "lldrawable.h"
 #include "llface.h"
 #include "llfloaterproperties.h"
+#include "llfloatertools.h"
 #include "llfollowcam.h"
 #include "llhudtext.h"
 #include "llselectmgr.h"
@@ -202,6 +204,11 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
 	mGLName(0),
 	mbCanSelect(TRUE),
 	mFlags(0),
+	mPhysicsShapeType(0),
+	mPhysicsGravity(0),
+	mPhysicsFriction(0),
+	mPhysicsDensity(0),
+	mPhysicsRestitution(0),
 	mDrawable(),
 	mCreateSelected(FALSE),
 	mRenderMedia(FALSE),
@@ -233,6 +240,12 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
 	mState(0),
 	mMedia(NULL),
 	mClickAction(0),
+	mObjectCost(0),
+	mLinksetCost(0),
+	mPhysicsCost(0),
+	mLinksetPhysicsCost(0.f),
+	mCostStale(true),
+	mPhysicsShapeUnknown(true),
 	mAttachmentItemID(LLUUID::null),
 	mLastUpdateType(OUT_UNKNOWN),
 	mLastUpdateCached(FALSE)
@@ -845,6 +858,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 #ifdef DEBUG_UPDATE_TYPE
 				llinfos << "Full:" << getID() << llendl;
 #endif
+				//clear cost and linkset cost
+				mCostStale = true;
+				if (isSelected())
+				{
+					gFloaterTools->dirty();
+				}
+
 				LLUUID audio_uuid;
 				LLUUID owner_id;	// only valid if audio_uuid or particle system is not null
 				F32    gain;
@@ -1410,6 +1430,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 #ifdef DEBUG_UPDATE_TYPE
 				llinfos << "CompFull:" << getID() << llendl;
 #endif
+				mCostStale = true;
+
+				if (isSelected())
+				{
+					gFloaterTools->dirty();
+				}
+	
 				dp->unpackU32(crc, "CRC");
 				mTotalCRC = crc;
 				dp->unpackU8(material, "Material");
@@ -3014,21 +3041,126 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
 	}
 }
 
-void LLViewerObject::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+void LLViewerObject::setObjectCost(F32 cost)
 {
-	LLVector3 center = getRenderPosition();
-	LLVector3 size = getScale();
-	newMin.setVec(center-size);
-	newMax.setVec(center+size);
-	mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+	mObjectCost = cost;
+	mCostStale = false;
+
+	if (isSelected())
+	{
+		gFloaterTools->dirty();
+	}
+}
+
+void LLViewerObject::setLinksetCost(F32 cost)
+{
+	mLinksetCost = cost;
+	mCostStale = false;
+	
+	if (isSelected())
+	{
+		gFloaterTools->dirty();
+	}
+}
+
+void LLViewerObject::setPhysicsCost(F32 cost)
+{
+	mPhysicsCost = cost;
+	mCostStale = false;
+
+	if (isSelected())
+	{
+		gFloaterTools->dirty();
+	}
+}
+
+void LLViewerObject::setLinksetPhysicsCost(F32 cost)
+{
+	mLinksetPhysicsCost = cost;
+	mCostStale = false;
+	
+	if (isSelected())
+	{
+		gFloaterTools->dirty();
+	}
+}
+
+
+F32 LLViewerObject::getObjectCost()
+{
+	if (mCostStale)
+	{
+		gObjectList.updateObjectCost(this);
+	}
+	
+	return mObjectCost;
+}
+
+F32 LLViewerObject::getLinksetCost()
+{
+	if (mCostStale)
+	{
+		gObjectList.updateObjectCost(this);
+	}
+
+	return mLinksetCost;
+}
+
+F32 LLViewerObject::getPhysicsCost()
+{
+	if (mCostStale)
+	{
+		gObjectList.updateObjectCost(this);
+	}
+	
+	return mPhysicsCost;
+}
+
+F32 LLViewerObject::getLinksetPhysicsCost()
+{
+	if (mCostStale)
+	{
+		gObjectList.updateObjectCost(this);
+	}
+
+	return mLinksetPhysicsCost;
+}
+
+F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes)
+{
+	return 0.f;
+}
+
+U32 LLViewerObject::getTriangleCount()
+{
+	return 0;
+}
+
+U32 LLViewerObject::getHighLODTriangleCount()
+{
+	return 0;
+}
+
+void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
+{
+	LLVector4a center;
+	center.load3(getRenderPosition().mV);
+	LLVector4a size;
+	size.load3(getScale().mV);
+	newMin.setSub(center, size);
+	newMax.setAdd(center, size);
+	
+	mDrawable->setPositionGroup(center);
 }
 
 F32 LLViewerObject::getBinRadius()
 {
 	if (mDrawable.notNull())
 	{
-		const LLVector3* ext = mDrawable->getSpatialExtents();
-		return (ext[1]-ext[0]).magVec();
+		const LLVector4a* ext = mDrawable->getSpatialExtents();
+		LLVector4a diff;
+		diff.setSub(ext[1], ext[0]);
+		return diff.getLength3().getF32();
 	}
 	
 	return getScale().magVec();
@@ -3094,7 +3226,7 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
  		getTEImage(i)->setBoostLevel(LLViewerTexture::BOOST_SELECTED);
 	}
 
-	if (isSculpted())
+	if (isSculpted() && !isMesh())
 	{
 		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
 		LLUUID sculpt_id = sculpt_params->getSculptTexture();
@@ -3334,6 +3466,15 @@ const LLVector3 LLViewerObject::getPositionEdit() const
 
 const LLVector3 LLViewerObject::getRenderPosition() const
 {
+	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
+	{
+		LLVOAvatar* avatar = getAvatar();
+		if (avatar)
+		{
+			return avatar->getPositionAgent();
+		}
+	}
+
 	if (mDrawable.isNull() || mDrawable->getGeneration() < 0)
 	{
 		return getPositionAgent();
@@ -3352,6 +3493,11 @@ const LLVector3 LLViewerObject::getPivotPositionAgent() const
 const LLQuaternion LLViewerObject::getRenderRotation() const
 {
 	LLQuaternion ret;
+	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
+	{
+		return ret;
+	}
+	
 	if (mDrawable.isNull() || mDrawable->isStatic())
 	{
 		ret = getRotationEdit();
@@ -3620,12 +3766,21 @@ BOOL LLViewerObject::lineSegmentBoundingBox(const LLVector3& start, const LLVect
 		return FALSE;
 	}
 
-	const LLVector3* ext = mDrawable->getSpatialExtents();
+	const LLVector4a* ext = mDrawable->getSpatialExtents();
 
-	LLVector3 center = (ext[1]+ext[0])*0.5f;
-	LLVector3 size = (ext[1]-ext[0])*0.5f;
+	//VECTORIZE THIS
+	LLVector4a center;
+	center.setAdd(ext[1], ext[0]);
+	center.mul(0.5f);
+	LLVector4a size;
+	size.setSub(ext[1], ext[0]);
+	size.mul(0.5f);
 
-	return LLLineSegmentBoxIntersect(start, end, center, size);
+	LLVector4a starta, enda;
+	starta.load3(start.mV);
+	enda.load3(end.mV);
+
+	return LLLineSegmentBoxIntersect(starta, enda, center, size);
 }
 
 U8 LLViewerObject::getMediaType() const
@@ -5140,6 +5295,12 @@ void LLViewerObject::updateFlags()
 	gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() );
 	gMessageSystem->addBOOL("IsPhantom", flagPhantom() );
 	gMessageSystem->addBOOL("CastsShadows", flagCastShadows() );
+	gMessageSystem->nextBlock("ExtraPhysics");
+	gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() );
+	gMessageSystem->addF32("Density", getPhysicsDensity() );
+	gMessageSystem->addF32("Friction", getPhysicsFriction() );
+	gMessageSystem->addF32("Restitution", getPhysicsRestitution() );
+	gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() );
 	gMessageSystem->sendReliable( regionp->getHost() );
 }
 
@@ -5172,6 +5333,44 @@ BOOL LLViewerObject::setFlags(U32 flags, BOOL state)
 	return setit;
 }
 
+void LLViewerObject::setPhysicsShapeType(U8 type)
+{
+	mPhysicsShapeUnknown = false;
+	mPhysicsShapeType = type;
+	mCostStale = true;
+}
+
+void LLViewerObject::setPhysicsGravity(F32 gravity)
+{
+	mPhysicsGravity = gravity;
+}
+
+void LLViewerObject::setPhysicsFriction(F32 friction)
+{
+	mPhysicsFriction = friction;
+}
+
+void LLViewerObject::setPhysicsDensity(F32 density)
+{
+	mPhysicsDensity = density;
+}
+
+void LLViewerObject::setPhysicsRestitution(F32 restitution)
+{
+	mPhysicsRestitution = restitution;
+}
+
+U8 LLViewerObject::getPhysicsShapeType() const
+{ 
+	if (mPhysicsShapeUnknown)
+	{
+		mPhysicsShapeUnknown = false;
+		gObjectList.updatePhysicsFlags(this);
+	}
+
+	return mPhysicsShapeType; 
+}
+
 void LLViewerObject::applyAngularVelocity(F32 dt)
 {
 	//do target omega here
@@ -5428,3 +5627,75 @@ const LLUUID &LLViewerObject::extractAttachmentItemID()
 	setAttachmentItemID(item_id);
 	return getAttachmentItemID();
 }
+
+//virtual
+LLVOAvatar* LLViewerObject::getAvatar() const
+{
+	if (isAttachment())
+	{
+		LLViewerObject* vobj = (LLViewerObject*) getParent();
+
+		while (vobj && !vobj->asAvatar())
+		{
+			vobj = (LLViewerObject*) vobj->getParent();
+		}
+
+		return (LLVOAvatar*) vobj;
+	}
+
+	return NULL;
+}
+
+
+class ObjectPhysicsProperties : public LLHTTPNode
+{
+public:
+	virtual void post(
+		ResponsePtr responder,
+		const LLSD& context,
+		const LLSD& input) const
+	{
+		LLSD object_data = input["body"]["ObjectData"];
+		S32 num_entries = object_data.size();
+		
+		for ( S32 i = 0; i < num_entries; i++ )
+		{
+			LLSD& curr_object_data = object_data[i];
+			U32 local_id = curr_object_data["LocalID"].asInteger();
+
+			// Iterate through nodes at end, since it can be on both the regular AND hover list
+			struct f : public LLSelectedNodeFunctor
+			{
+				U32 mID;
+				f(const U32& id) : mID(id) {}
+				virtual bool apply(LLSelectNode* node)
+				{
+					return (node->getObject() && node->getObject()->mLocalID == mID );
+				}
+			} func(local_id);
+
+			LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func);
+
+			if (node)
+			{
+				// The LLSD message builder doesn't know how to handle U8, so we need to send as S8 and cast
+				U8 type = (U8)curr_object_data["PhysicsShapeType"].asInteger();
+				F32 density = (F32)curr_object_data["Density"].asReal();
+				F32 friction = (F32)curr_object_data["Friction"].asReal();
+				F32 restitution = (F32)curr_object_data["Restitution"].asReal();
+				F32 gravity = (F32)curr_object_data["GravityMultiplier"].asReal();
+
+				node->getObject()->setPhysicsShapeType(type);
+				node->getObject()->setPhysicsGravity(gravity);
+				node->getObject()->setPhysicsFriction(friction);
+				node->getObject()->setPhysicsDensity(density);
+				node->getObject()->setPhysicsRestitution(restitution);
+			}	
+		}
+		
+		dialog_refresh_all();
+	};
+};
+
+LLHTTPRegistration<ObjectPhysicsProperties>
+	gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties");
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 7afb7f464bb9cd747f3896812b95467a9c1d907a..e417343becb07402ec4755c4fd80ca198b260cfe 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -176,6 +176,7 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	void			setOnActiveList(BOOL on_active)		{ mOnActiveList = on_active; }
 
 	virtual BOOL	isAttachment() const { return FALSE; }
+	virtual LLVOAvatar* getAvatar() const;  //get the avatar this object is attached to, or NULL if object is not an attachment
 	virtual BOOL	isHUDAttachment() const { return FALSE; }
 	virtual void 	updateRadius() {};
 	virtual F32 	getVObjRadius() const; // default implemenation is mDrawable->getRadius()
@@ -224,6 +225,7 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 
 	virtual BOOL isFlexible() const					{ return FALSE; }
 	virtual BOOL isSculpted() const 				{ return FALSE; }
+	virtual BOOL isMesh() const						{ return FALSE; }
 	virtual BOOL hasLightTexture() const			{ return FALSE; }
 
 	// This method returns true if the object is over land owned by
@@ -324,6 +326,22 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	
 	virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE);
 
+	virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL);
+	virtual U32 getTriangleCount();
+	virtual U32 getHighLODTriangleCount();
+
+	void setObjectCost(F32 cost);
+	F32 getObjectCost();
+	
+	void setLinksetCost(F32 cost);
+	F32 getLinksetCost();
+	
+	void setPhysicsCost(F32 cost);
+	F32 getPhysicsCost();
+	
+	void setLinksetPhysicsCost(F32 cost);
+	F32 getLinksetPhysicsCost();
+
 	void sendShapeUpdate();
 
 	U8 getState()							{ return mState; }
@@ -363,7 +381,7 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 
 	void markForUpdate(BOOL priority);
 	void updateVolume(const LLVolumeParams& volume_params);
-	virtual	void updateSpatialExtents(LLVector3& min, LLVector3& max);
+	virtual	void updateSpatialExtents(LLVector4a& min, LLVector4a& max);
 	virtual F32 getBinRadius();
 	
 	LLBBox				getBoundingBoxAgent() const;
@@ -376,7 +394,7 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	void clearDrawableState(U32 state, BOOL recursive = TRUE);
 
 	// Called when the drawable shifts
-	virtual void onShift(const LLVector3 &shift_vector)	{ }
+	virtual void onShift(const LLVector4a &shift_vector)	{ }
 		
 	//////////////////////////////////////
 	//
@@ -451,6 +469,12 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	inline BOOL		flagCameraDecoupled() const		{ return ((mFlags & FLAGS_CAMERA_DECOUPLED) != 0); }
 	inline BOOL		flagObjectMove() const			{ return ((mFlags & FLAGS_OBJECT_MOVE) != 0); }
 
+	U8       getPhysicsShapeType() const;
+	inline F32      getPhysicsGravity() const       { return mPhysicsGravity; }
+	inline F32      getPhysicsFriction() const      { return mPhysicsFriction; }
+	inline F32      getPhysicsDensity() const       { return mPhysicsDensity; }
+	inline F32      getPhysicsRestitution() const   { return mPhysicsRestitution; }
+	
 	bool getIncludeInSearch() const;
 	void setIncludeInSearch(bool include_in_search);
 
@@ -466,6 +490,11 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 
 	void updateFlags();
 	BOOL setFlags(U32 flag, BOOL state);
+	void setPhysicsShapeType(U8 type);
+	void setPhysicsGravity(F32 gravity);
+	void setPhysicsFriction(F32 friction);
+	void setPhysicsDensity(F32 density);
+	void setPhysicsRestitution(F32 restitution);
 	
 	virtual void dump() const;
 	static U32		getNumZombieObjects()			{ return sNumZombieObjects; }
@@ -530,6 +559,13 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 		LL_VO_HUD_PART_GROUP =		LL_PCODE_APP | 0xc0,
 	} EVOType;
 
+	typedef enum e_physics_shape_types
+	{
+		PHYSICS_SHAPE_PRIM = 0,
+		PHYSICS_SHAPE_NONE,
+		PHYSICS_SHAPE_CONVEX_HULL,
+	} EPhysicsShapeType;
+
 	LLUUID			mID;
 
 	// unique within region, not unique across regions
@@ -548,6 +584,14 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	// Grabbed from UPDATE_FLAGS
 	U32				mFlags;
 
+	// Sent to sim in UPDATE_FLAGS, received in ObjectPhysicsProperties
+	U8              mPhysicsShapeType;
+	F32             mPhysicsGravity;
+	F32             mPhysicsFriction;
+	F32             mPhysicsDensity;
+	F32             mPhysicsRestitution;
+	
+
 	// Pipeline classes
 	LLPointer<LLDrawable> mDrawable;
 
@@ -656,6 +700,13 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	U8				mState;	// legacy
 	LLViewerObjectMedia* mMedia;	// NULL if no media associated
 	U8 mClickAction;
+	F32 mObjectCost; //resource cost of this object or -1 if unknown
+	F32 mLinksetCost;
+	F32 mPhysicsCost;
+	F32 mLinksetPhysicsCost;
+
+	bool mCostStale;
+	mutable bool mPhysicsShapeUnknown;
 
 	static			U32			sNumZombieObjects;			// Objects which are dead, but not deleted
 
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index da95bacc41e6dca3f271ff399554a9b2276cd0e6..ab2e07e4df010b7cc2a9dbe9027dadc8810e567d 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -54,6 +54,7 @@
 #include "llviewercamera.h"
 #include "llselectmgr.h"
 #include "llresmgr.h"
+#include "llsdutil.h"
 #include "llviewerregion.h"
 #include "llviewerstats.h"
 #include "llviewerstatsrecorder.h"
@@ -692,6 +693,189 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
 	LLVOAvatar::cullAvatarsByPixelArea();
 }
 
+class LLObjectCostResponder : public LLCurl::Responder
+{
+public:
+	LLObjectCostResponder(const LLSD& object_ids)
+		: mObjectIDs(object_ids)
+	{
+	}
+
+	// Clear's the global object list's pending
+	// request list for all objects requested
+	void clear_object_list_pending_requests()
+	{
+		// TODO*: No more hard coding
+		for (
+			LLSD::array_iterator iter = mObjectIDs.beginArray();
+			iter != mObjectIDs.endArray();
+			++iter)
+		{
+			gObjectList.onObjectCostFetchFailure(iter->asUUID());
+		}
+	}
+
+	void error(U32 statusNum, const std::string& reason)
+	{
+		llwarns
+			<< "Transport error requesting object cost "
+			<< "HTTP status: " << statusNum << ", reason: "
+			<< reason << "." << llendl;
+
+		// TODO*: Error message to user
+		// For now just clear the request from the pending list
+		clear_object_list_pending_requests();
+	}
+
+	void result(const LLSD& content)
+	{
+		if ( !content.isMap() || content.has("error") )
+		{
+			// Improper response or the request had an error,
+			// show an error to the user?
+			llwarns
+				<< "Application level error when fetching object "
+				<< "cost.  Message: " << content["error"]["message"].asString()
+				<< ", identifier: " << content["error"]["identifier"].asString()
+				<< llendl;
+
+			// TODO*: Adaptively adjust request size if the
+			// service says we've requested too many and retry
+
+			// TODO*: Error message if not retrying
+			clear_object_list_pending_requests();
+			return;
+		}
+
+		// Success, grab the resource cost and linked set costs
+		// for an object if one was returned
+		for (
+			LLSD::array_iterator iter = mObjectIDs.beginArray();
+			iter != mObjectIDs.endArray();
+			++iter)
+		{
+			LLUUID object_id = iter->asUUID();
+
+			// Check to see if the request contains data for the object
+			if ( content.has(iter->asString()) )
+			{
+				F32 link_cost =
+					content[iter->asString()]["linked_set_resource_cost"].asReal();
+				F32 object_cost =
+					content[iter->asString()]["resource_cost"].asReal();
+
+				F32 physics_cost = content[iter->asString()]["physics_cost"].asReal();
+				F32 link_physics_cost = content[iter->asString()]["linked_set_physics_cost"].asReal();
+
+				gObjectList.updateObjectCost(object_id, object_cost, link_cost, physics_cost, link_physics_cost);
+			}
+			else
+			{
+				// TODO*: Give user feedback about the missing data?
+				gObjectList.onObjectCostFetchFailure(object_id);
+			}
+		}
+	}
+
+private:
+	LLSD mObjectIDs;
+};
+
+
+class LLPhysicsFlagsResponder : public LLCurl::Responder
+{
+public:
+	LLPhysicsFlagsResponder(const LLSD& object_ids)
+		: mObjectIDs(object_ids)
+	{
+	}
+
+	// Clear's the global object list's pending
+	// request list for all objects requested
+	void clear_object_list_pending_requests()
+	{
+		// TODO*: No more hard coding
+		for (
+			LLSD::array_iterator iter = mObjectIDs.beginArray();
+			iter != mObjectIDs.endArray();
+			++iter)
+		{
+			gObjectList.onPhysicsFlagsFetchFailure(iter->asUUID());
+		}
+	}
+
+	void error(U32 statusNum, const std::string& reason)
+	{
+		llwarns
+			<< "Transport error requesting object physics flags "
+			<< "HTTP status: " << statusNum << ", reason: "
+			<< reason << "." << llendl;
+
+		// TODO*: Error message to user
+		// For now just clear the request from the pending list
+		clear_object_list_pending_requests();
+	}
+
+	void result(const LLSD& content)
+	{
+		if ( !content.isMap() || content.has("error") )
+		{
+			// Improper response or the request had an error,
+			// show an error to the user?
+			llwarns
+				<< "Application level error when fetching object "
+				<< "physics flags.  Message: " << content["error"]["message"].asString()
+				<< ", identifier: " << content["error"]["identifier"].asString()
+				<< llendl;
+
+			// TODO*: Adaptively adjust request size if the
+			// service says we've requested too many and retry
+
+			// TODO*: Error message if not retrying
+			clear_object_list_pending_requests();
+			return;
+		}
+
+		// Success, grab the resource cost and linked set costs
+		// for an object if one was returned
+		for (
+			LLSD::array_iterator iter = mObjectIDs.beginArray();
+			iter != mObjectIDs.endArray();
+			++iter)
+		{
+			LLUUID object_id = iter->asUUID();
+
+			// Check to see if the request contains data for the object
+			if ( content.has(iter->asString()) )
+			{
+				const LLSD& data = content[iter->asString()];
+
+				S32 shape_type = data["PhysicsShapeType"].asInteger();
+
+				gObjectList.updatePhysicsShapeType(object_id, shape_type);
+
+				if (data.has("Density"))
+				{
+					F32 density = data["Density"].asReal();
+					F32 friction = data["Friction"].asReal();
+					F32 restitution = data["Restitution"].asReal();
+					F32 gravity_multiplier = data["GravityMultiplier"].asReal();
+					
+					gObjectList.updatePhysicsProperties(object_id, 
+						density, friction, restitution, gravity_multiplier);
+				}
+			}
+			else
+			{
+				// TODO*: Give user feedback about the missing data?
+				gObjectList.onPhysicsFlagsFetchFailure(object_id);
+			}
+		}
+	}
+
+private:
+	LLSD mObjectIDs;
+};
 
 void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
 {
@@ -804,6 +988,9 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
 		}
 	}
 
+	fetchObjectCosts();
+	fetchPhysicsFlags();
+
 	mNumSizeCulled = 0;
 	mNumVisCulled = 0;
 
@@ -869,6 +1056,119 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
 	LLViewerStats::getInstance()->mNumVisCulledStat.addValue(mNumVisCulled);
 }
 
+void LLViewerObjectList::fetchObjectCosts()
+{
+	// issue http request for stale object physics costs
+	if (!mStaleObjectCost.empty())
+	{
+		LLViewerRegion* regionp = gAgent.getRegion();
+
+		if (regionp)
+		{
+			std::string url = regionp->getCapability("GetObjectCost");
+
+			if (!url.empty())
+			{
+				LLSD id_list;
+				U32 object_index = 0;
+
+				for (
+					std::set<LLUUID>::iterator iter = mStaleObjectCost.begin();
+					iter != mStaleObjectCost.end();
+					++iter)
+				{
+					// Check to see if a request for this object
+					// has already been made.
+					if ( mPendingObjectCost.find(*iter) ==
+						 mPendingObjectCost.end() )
+					{
+						mPendingObjectCost.insert(*iter);
+						id_list[object_index++] = *iter;
+					}
+				}
+
+				// id_list should now contain all
+				// requests in mStaleObjectCost before, so clear
+				// it now
+				mStaleObjectCost.clear();
+
+				if ( id_list.size() > 0 )
+				{
+					LLSD post_data = LLSD::emptyMap();
+
+					post_data["object_ids"] = id_list;
+					LLHTTPClient::post(
+						url,
+						post_data,
+						new LLObjectCostResponder(id_list));
+				}
+			}
+			else
+			{
+				mStaleObjectCost.clear();
+				mPendingObjectCost.clear();
+			}
+		}
+	}
+}
+
+void LLViewerObjectList::fetchPhysicsFlags()
+{
+	// issue http request for stale object physics flags
+	if (!mStalePhysicsFlags.empty())
+	{
+		LLViewerRegion* regionp = gAgent.getRegion();
+
+		if (regionp)
+		{
+			std::string url = regionp->getCapability("GetObjectPhysicsData");
+
+			if (!url.empty())
+			{
+				LLSD id_list;
+				U32 object_index = 0;
+
+				for (
+					std::set<LLUUID>::iterator iter = mStalePhysicsFlags.begin();
+					iter != mStalePhysicsFlags.end();
+					++iter)
+				{
+					// Check to see if a request for this object
+					// has already been made.
+					if ( mPendingPhysicsFlags.find(*iter) ==
+						 mPendingPhysicsFlags.end() )
+					{
+						mPendingPhysicsFlags.insert(*iter);
+						id_list[object_index++] = *iter;
+					}
+				}
+
+				// id_list should now contain all
+				// requests in mStalePhysicsFlags before, so clear
+				// it now
+				mStalePhysicsFlags.clear();
+
+				if ( id_list.size() > 0 )
+				{
+					LLSD post_data = LLSD::emptyMap();
+
+					post_data["object_ids"] = id_list;
+					LLHTTPClient::post(
+						url,
+						post_data,
+						new LLPhysicsFlagsResponder(id_list));
+				}
+			}
+			else
+			{
+				mStalePhysicsFlags.clear();
+				mPendingPhysicsFlags.clear();
+			}
+		}
+	}
+}
+
+
 void LLViewerObjectList::clearDebugText()
 {
 	for (vobj_list_t::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)
@@ -923,8 +1223,12 @@ void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
 	mNumDeadObjects++;
 }
 
+static LLFastTimer::DeclareTimer FTM_REMOVE_DRAWABLE("Remove Drawable");
+
 void LLViewerObjectList::removeDrawable(LLDrawable* drawablep)
 {
+	LLFastTimer t(FTM_REMOVE_DRAWABLE);
+
 	if (!drawablep)
 	{
 		return;
@@ -1089,7 +1393,69 @@ void LLViewerObjectList::updateActive(LLViewerObject *objectp)
 	}
 }
 
+void LLViewerObjectList::updateObjectCost(LLViewerObject* object)
+{
+	mStaleObjectCost.insert(object->getID());
+}
+
+void LLViewerObjectList::updateObjectCost(const LLUUID& object_id, F32 object_cost, F32 link_cost, F32 physics_cost, F32 link_physics_cost)
+{
+	mPendingObjectCost.erase(object_id);
 
+	LLViewerObject* object = findObject(object_id);
+	if (object)
+	{
+		object->setObjectCost(object_cost);
+		object->setLinksetCost(link_cost);
+		object->setPhysicsCost(physics_cost);
+		object->setLinksetPhysicsCost(link_physics_cost);
+	}
+}
+
+void LLViewerObjectList::onObjectCostFetchFailure(const LLUUID& object_id)
+{
+	//llwarns << "Failed to fetch object cost for object: " << object_id << llendl;
+	mPendingObjectCost.erase(object_id);
+}
+
+void LLViewerObjectList::updatePhysicsFlags(const LLViewerObject* object)
+{
+	mStalePhysicsFlags.insert(object->getID());
+}
+
+void LLViewerObjectList::updatePhysicsShapeType(const LLUUID& object_id, S32 type)
+{
+	mPendingPhysicsFlags.erase(object_id);
+	LLViewerObject* object = findObject(object_id);
+	if (object)
+	{
+		object->setPhysicsShapeType(type);
+	}
+}
+
+void LLViewerObjectList::updatePhysicsProperties(const LLUUID& object_id, 
+												F32 density,
+												F32 friction,
+												F32 restitution,
+												F32 gravity_multiplier)
+{
+	mPendingPhysicsFlags.erase(object_id);
+
+	LLViewerObject* object = findObject(object_id);
+	if (object)
+	{
+		object->setPhysicsDensity(density);
+		object->setPhysicsFriction(friction);
+		object->setPhysicsGravity(gravity_multiplier);
+		object->setPhysicsRestitution(restitution);
+	}
+}
+
+void LLViewerObjectList::onPhysicsFlagsFetchFailure(const LLUUID& object_id)
+{
+	//llwarns << "Failed to fetch physics flags for object: " << object_id << llendl;
+	mPendingPhysicsFlags.erase(object_id);
+}
 
 void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
 {
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index 22a7f97c38e5474aad34ef01e28654efa6b8340a..65374bca70f89f6170abd20c2845ff6f2bbea075 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -85,6 +85,22 @@ class LLViewerObjectList
 	void updateApparentAngles(LLAgent &agent);
 	void update(LLAgent &agent, LLWorld &world);
 
+	void fetchObjectCosts();
+	void fetchPhysicsFlags();
+
+	void updateObjectCost(LLViewerObject* object);
+	void updateObjectCost(const LLUUID& object_id, F32 object_cost, F32 link_cost, F32 physics_cost, F32 link_physics_cost);
+	void onObjectCostFetchFailure(const LLUUID& object_id);
+
+	void updatePhysicsFlags(const LLViewerObject* object);
+	void onPhysicsFlagsFetchFailure(const LLUUID& object_id);
+	void updatePhysicsShapeType(const LLUUID& object_id, S32 type);
+	void updatePhysicsProperties(const LLUUID& object_id,
+									F32 density,
+									F32 friction,
+									F32 restitution,
+									F32 gravity_multiplier);
+
 	void shiftObjects(const LLVector3 &offset);
 
 	bool hasMapObjectInRegion(LLViewerRegion* regionp) ;
@@ -186,6 +202,14 @@ class LLViewerObjectList
 
 	std::map<LLUUID, LLPointer<LLViewerObject> > mUUIDObjectMap;
 
+	//set of objects that need to update their cost
+	std::set<LLUUID> mStaleObjectCost;
+	std::set<LLUUID> mPendingObjectCost;
+
+	//set of objects that need to update their physics flags
+	std::set<LLUUID> mStalePhysicsFlags;
+	std::set<LLUUID> mPendingPhysicsFlags;
+
 	std::vector<LLDebugBeacon> mDebugBeacons;
 
 	S32 mCurLazyUpdateIndex;
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
index 4fee85e45cc4071ff1c761110e5976e5429bcc90..6b3e04348a1c386fb8bd174d50945ae87165394e 100644
--- a/indra/newview/llviewerpartsim.cpp
+++ b/indra/newview/llviewerpartsim.cpp
@@ -155,8 +155,8 @@ LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 bo
 
 	if (group != NULL)
 	{
-		LLVector3 center(group->mOctreeNode->getCenter());
-		LLVector3 size(group->mOctreeNode->getSize());
+		LLVector3 center(group->mOctreeNode->getCenter().getF32ptr());
+		LLVector3 size(group->mOctreeNode->getSize().getF32ptr());
 		size += LLVector3(0.01f, 0.01f, 0.01f);
 		mMinObjPos = center - size;
 		mMaxObjPos = center + size;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 8909abf36ef42c7e1aeb9af7cf14584302306cb5..f835351c04306f7047363afc238be2ee9b25e308 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1185,7 +1185,7 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObjec
 // AND the CRC matches. JC
 LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc, U8 &cache_miss_type)
 {
-	llassert(mCacheLoaded);
+	//llassert(mCacheLoaded);  This assert failes often, changing to early-out -- davep, 2010/10/18
 
 	LLVOCacheEntry* entry = get_if_there(mImpl->mCacheMap, local_id, (LLVOCacheEntry*)NULL);
 
@@ -1196,22 +1196,23 @@ LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc, U8 &cache_miss_type)
 		{
 			// Record a hit
 			entry->recordHit();
-			cache_miss_type = CACHE_MISS_TYPE_NONE;
+		cache_miss_type = CACHE_MISS_TYPE_NONE;
 			return entry->getDP(crc);
 		}
 		else
 		{
 			// llinfos << "CRC miss for " << local_id << llendl;
-			cache_miss_type = CACHE_MISS_TYPE_CRC;
+		cache_miss_type = CACHE_MISS_TYPE_CRC;
 			mCacheMissCRC.put(local_id);
 		}
 	}
 	else
 	{
 		// llinfos << "Cache miss for " << local_id << llendl;
-		cache_miss_type = CACHE_MISS_TYPE_FULL;
+	cache_miss_type = CACHE_MISS_TYPE_FULL;
 		mCacheMissFull.put(local_id);
 	}
+
 	return NULL;
 }
 
@@ -1499,12 +1500,17 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
 
 	capabilityNames.append("GetDisplayNames");
 	capabilityNames.append("GetTexture");
+	capabilityNames.append("GetMesh");
+	capabilityNames.append("GetObjectCost");
+	capabilityNames.append("GetObjectPhysicsData");
 	capabilityNames.append("GroupProposalBallot");
 	capabilityNames.append("HomeLocation");
 	capabilityNames.append("LandResources");
 	capabilityNames.append("MapLayer");
 	capabilityNames.append("MapLayerGod");
 	capabilityNames.append("NewFileAgentInventory");
+	capabilityNames.append("NewFileAgentInventoryVariablePrice");
+	capabilityNames.append("ObjectAdd");
 	capabilityNames.append("ParcelPropertiesUpdate");
 	capabilityNames.append("ParcelMediaURLFilterList");
 	capabilityNames.append("ParcelNavigateMedia");
@@ -1519,6 +1525,8 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
 	capabilityNames.append("SendUserReport");
 	capabilityNames.append("SendUserReportWithScreenshot");
 	capabilityNames.append("ServerReleaseNotes");
+	capabilityNames.append("SimConsole");
+	capabilityNames.append("SimulatorFeatures");
 	capabilityNames.append("SetDisplayName");
 	capabilityNames.append("SimConsoleAsync");
 	capabilityNames.append("StartGroupProposal");
@@ -1533,6 +1541,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
 	capabilityNames.append("UpdateNotecardTaskInventory");
 	capabilityNames.append("UpdateScriptTask");
 	capabilityNames.append("UploadBakedTexture");
+	capabilityNames.append("UploadObjectAsset");
 	capabilityNames.append("ViewerMetrics");
 	capabilityNames.append("ViewerStartAuction");
 	capabilityNames.append("ViewerStats");
@@ -1579,6 +1588,7 @@ std::string LLViewerRegion::getCapability(const std::string& name) const
 	{
 		return "";
 	}
+
 	return iter->second;
 }
 
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index c1abead36e4c9d10ae9fe4cad57cd4901f4d9eca..3e85802ba6c630780e6e4cab4676e482e9caa27d 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -68,9 +68,21 @@ LLGLSLShader		gObjectFullbrightProgram;
 LLGLSLShader		gObjectFullbrightWaterProgram;
 
 LLGLSLShader		gObjectFullbrightShinyProgram;
+LLGLSLShader		gObjectFullbrightShinyWaterProgram;
 LLGLSLShader		gObjectShinyProgram;
 LLGLSLShader		gObjectShinyWaterProgram;
 
+//object hardware skinning shaders
+LLGLSLShader		gSkinnedObjectSimpleProgram;
+LLGLSLShader		gSkinnedObjectFullbrightProgram;
+LLGLSLShader		gSkinnedObjectFullbrightShinyProgram;
+LLGLSLShader		gSkinnedObjectShinySimpleProgram;
+
+LLGLSLShader		gSkinnedObjectSimpleWaterProgram;
+LLGLSLShader		gSkinnedObjectFullbrightWaterProgram;
+LLGLSLShader		gSkinnedObjectFullbrightShinyWaterProgram;
+LLGLSLShader		gSkinnedObjectShinySimpleWaterProgram;
+
 //environment shaders
 LLGLSLShader		gTerrainProgram;
 LLGLSLShader		gTerrainWaterProgram;
@@ -101,6 +113,9 @@ LLGLSLShader			gDeferredImpostorProgram;
 LLGLSLShader			gDeferredEdgeProgram;
 LLGLSLShader			gDeferredWaterProgram;
 LLGLSLShader			gDeferredDiffuseProgram;
+LLGLSLShader			gDeferredSkinnedDiffuseProgram;
+LLGLSLShader			gDeferredSkinnedBumpProgram;
+LLGLSLShader			gDeferredSkinnedAlphaProgram;
 LLGLSLShader			gDeferredBumpProgram;
 LLGLSLShader			gDeferredTerrainProgram;
 LLGLSLShader			gDeferredTreeProgram;
@@ -115,12 +130,14 @@ LLGLSLShader			gDeferredBlurLightProgram;
 LLGLSLShader			gDeferredSoftenProgram;
 LLGLSLShader			gDeferredShadowProgram;
 LLGLSLShader			gDeferredAvatarShadowProgram;
+LLGLSLShader			gDeferredAttachmentShadowProgram;
 LLGLSLShader			gDeferredAlphaProgram;
 LLGLSLShader			gDeferredFullbrightProgram;
 LLGLSLShader			gDeferredGIProgram;
 LLGLSLShader			gDeferredGIFinalProgram;
 LLGLSLShader			gDeferredPostGIProgram;
 LLGLSLShader			gDeferredPostProgram;
+LLGLSLShader			gDeferredPostNoDoFProgram;
 
 LLGLSLShader			gLuminanceGatherProgram;
 
@@ -142,6 +159,15 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
 	mShaderList.push_back(&gObjectSimpleProgram);
 	mShaderList.push_back(&gObjectFullbrightProgram);
 	mShaderList.push_back(&gObjectFullbrightShinyProgram);
+	mShaderList.push_back(&gObjectFullbrightShinyWaterProgram);
+	mShaderList.push_back(&gSkinnedObjectSimpleProgram);
+	mShaderList.push_back(&gSkinnedObjectFullbrightProgram);
+	mShaderList.push_back(&gSkinnedObjectFullbrightShinyProgram);
+	mShaderList.push_back(&gSkinnedObjectShinySimpleProgram);
+	mShaderList.push_back(&gSkinnedObjectSimpleWaterProgram);
+	mShaderList.push_back(&gSkinnedObjectFullbrightWaterProgram);
+	mShaderList.push_back(&gSkinnedObjectFullbrightShinyWaterProgram);
+	mShaderList.push_back(&gSkinnedObjectShinySimpleWaterProgram);
 	mShaderList.push_back(&gTerrainProgram);
 	mShaderList.push_back(&gTerrainWaterProgram);
 	mShaderList.push_back(&gObjectSimpleWaterProgram);
@@ -155,6 +181,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
 	mShaderList.push_back(&gDeferredLightProgram);
 	mShaderList.push_back(&gDeferredMultiLightProgram);
 	mShaderList.push_back(&gDeferredAlphaProgram);
+	mShaderList.push_back(&gDeferredSkinnedAlphaProgram);
 	mShaderList.push_back(&gDeferredFullbrightProgram);
 	mShaderList.push_back(&gDeferredPostGIProgram);
 	mShaderList.push_back(&gDeferredEdgeProgram);
@@ -189,6 +216,7 @@ void LLViewerShaderMgr::initAttribsAndUniforms(void)
 		mReservedAttribs.push_back("materialColor");
 		mReservedAttribs.push_back("specularColor");
 		mReservedAttribs.push_back("binormal");
+		mReservedAttribs.push_back("object_weight");
 
 		mAvatarAttribs.reserve(5);
 		mAvatarAttribs.push_back("weight");
@@ -370,7 +398,9 @@ void LLViewerShaderMgr::setShaders()
 		S32 deferred_class = 0;
 		
 		if (LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") &&
-		    gSavedSettings.getBOOL("RenderDeferred"))
+		    gSavedSettings.getBOOL("RenderDeferred") &&
+			gSavedSettings.getBOOL("RenderAvatarVP") &&
+			gSavedSettings.getBOOL("WindLightUseAtmosShaders"))
 		{
 			if (gSavedSettings.getS32("RenderShadowDetail") > 0)
 			{
@@ -388,14 +418,11 @@ void LLViewerShaderMgr::setShaders()
 				deferred_class = 1;
 			}
 
-			//make sure framebuffer objects are enabled
-			gSavedSettings.setBOOL("RenderUseFBO", TRUE);
-
 			//make sure hardware skinning is enabled
-			gSavedSettings.setBOOL("RenderAvatarVP", TRUE);
+			//gSavedSettings.setBOOL("RenderAvatarVP", TRUE);
 			
 			//make sure atmospheric shaders are enabled
-			gSavedSettings.setBOOL("WindLightUseAtmosShaders", TRUE);
+			//gSavedSettings.setBOOL("WindLightUseAtmosShaders", TRUE);
 		}
 
 
@@ -438,7 +465,6 @@ void LLViewerShaderMgr::setShaders()
 			// Load all shaders to set max levels
 			loadShadersEnvironment();
 			loadShadersWater();
-			loadShadersObject();
 			loadShadersWindLight();
 			loadShadersEffects();
 			loadShadersInterface();
@@ -446,14 +472,9 @@ void LLViewerShaderMgr::setShaders()
 			// Load max avatar shaders to set the max level
 			mVertexShaderLevel[SHADER_AVATAR] = 3;
 			mMaxAvatarShaderLevel = 3;
-			loadShadersAvatar();
-			
-#if 0 && LL_DARWIN // force avatar shaders off for mac
-			mVertexShaderLevel[SHADER_AVATAR] = 0;
-			sMaxAvatarShaderLevel = 0;
-#else
-			if (gSavedSettings.getBOOL("RenderAvatarVP"))
-			{
+						
+			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;
 				
@@ -484,17 +505,28 @@ void LLViewerShaderMgr::setShaders()
 				}
 			}
 			else
-			{
+			{ //hardware skinning not possible, neither is deferred rendering
 				mVertexShaderLevel[SHADER_AVATAR] = 0;
-				gSavedSettings.setBOOL("RenderAvatarCloth", FALSE);
+				mVertexShaderLevel[SHADER_DEFERRED] = 0;
+
+				if (gSavedSettings.getBOOL("RenderAvatarVP"))
+				{
+					gSavedSettings.setBOOL("RenderDeferred", FALSE);
+					gSavedSettings.setBOOL("RenderAvatarCloth", FALSE);
+					gSavedSettings.setBOOL("RenderAvatarVP", FALSE);
+				}
+
 				loadShadersAvatar(); // unloads
+				loadShadersObject();
 			}
 
 			if (!loadShadersDeferred())
 			{
 				gSavedSettings.setBOOL("RenderDeferred", FALSE);
+				reentrance = false;
+				setShaders();
+				return;
 			}
-#endif
 		}
 		else
 		{
@@ -542,7 +574,20 @@ void LLViewerShaderMgr::unloadShaders()
 
 	gObjectShinyProgram.unload();
 	gObjectFullbrightShinyProgram.unload();
+	gObjectFullbrightShinyWaterProgram.unload();
 	gObjectShinyWaterProgram.unload();
+
+	gSkinnedObjectSimpleProgram.unload();
+	gSkinnedObjectFullbrightProgram.unload();
+	gSkinnedObjectFullbrightShinyProgram.unload();
+	gSkinnedObjectShinySimpleProgram.unload();
+	
+	gSkinnedObjectSimpleWaterProgram.unload();
+	gSkinnedObjectFullbrightWaterProgram.unload();
+	gSkinnedObjectFullbrightShinyWaterProgram.unload();
+	gSkinnedObjectShinySimpleWaterProgram.unload();
+	
+
 	gWaterProgram.unload();
 	gUnderWaterProgram.unload();
 	gTerrainProgram.unload();
@@ -562,6 +607,9 @@ void LLViewerShaderMgr::unloadShaders()
 	gPostNightVisionProgram.unload();
 
 	gDeferredDiffuseProgram.unload();
+	gDeferredSkinnedDiffuseProgram.unload();
+	gDeferredSkinnedBumpProgram.unload();
+	gDeferredSkinnedAlphaProgram.unload();
 
 	mVertexShaderLevel[SHADER_LIGHTING] = 0;
 	mVertexShaderLevel[SHADER_OBJECT] = 0;
@@ -620,6 +668,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 	shaders.push_back( make_pair( "lighting/lightSpecularV.glsl",			mVertexShaderLevel[SHADER_LIGHTING] ) );
 	shaders.push_back( make_pair( "windlight/atmosphericsV.glsl",			mVertexShaderLevel[SHADER_WINDLIGHT] ) );
 	shaders.push_back( make_pair( "avatar/avatarSkinV.glsl",				1 ) );
+	shaders.push_back( make_pair( "avatar/objectSkinV.glsl",				1 ) );
 
 	// We no longer have to bind the shaders to global glhandles, they are automatically added to a map now.
 	for (U32 i = 0; i < shaders.size(); i++)
@@ -635,7 +684,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 	// (in order of shader function call depth for reference purposes, deepest level first)
 
 	shaders.clear();
-	shaders.reserve(12);
+	shaders.reserve(13);
 	shaders.push_back( make_pair( "windlight/atmosphericsVarsF.glsl",		mVertexShaderLevel[SHADER_WINDLIGHT] ) );
 	shaders.push_back( make_pair( "windlight/gammaF.glsl",					mVertexShaderLevel[SHADER_WINDLIGHT]) );
 	shaders.push_back( make_pair( "windlight/atmosphericsF.glsl",			mVertexShaderLevel[SHADER_WINDLIGHT] ) );
@@ -648,6 +697,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 	shaders.push_back( make_pair( "lighting/lightShinyF.glsl",				mVertexShaderLevel[SHADER_LIGHTING] ) );
 	shaders.push_back( make_pair( "lighting/lightFullbrightShinyF.glsl",	mVertexShaderLevel[SHADER_LIGHTING] ) );
 	shaders.push_back( make_pair( "lighting/lightShinyWaterF.glsl",			mVertexShaderLevel[SHADER_LIGHTING] ) );
+	shaders.push_back( make_pair( "lighting/lightFullbrightShinyWaterF.glsl", mVertexShaderLevel[SHADER_LIGHTING] ) );
 	
 	for (U32 i = 0; i < shaders.size(); i++)
 	{
@@ -875,6 +925,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 	{
 		gDeferredTreeProgram.unload();
 		gDeferredDiffuseProgram.unload();
+		gDeferredSkinnedDiffuseProgram.unload();
+		gDeferredSkinnedBumpProgram.unload();
+		gDeferredSkinnedAlphaProgram.unload();
 		gDeferredBumpProgram.unload();
 		gDeferredImpostorProgram.unload();
 		gDeferredTerrainProgram.unload();
@@ -887,6 +940,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredSoftenProgram.unload();
 		gDeferredShadowProgram.unload();
 		gDeferredAvatarShadowProgram.unload();
+		gDeferredAttachmentShadowProgram.unload();
 		gDeferredAvatarProgram.unload();
 		gDeferredAvatarAlphaProgram.unload();
 		gDeferredAlphaProgram.unload();
@@ -898,7 +952,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredGIProgram.unload();
 		gDeferredGIFinalProgram.unload();
 		gDeferredWaterProgram.unload();
-		return FALSE;
+		return TRUE;
 	}
 
 	mVertexShaderLevel[SHADER_AVATAR] = 1;
@@ -915,6 +969,44 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		success = gDeferredDiffuseProgram.createShader(NULL, NULL);
 	}
 
+	if (success)
+	{
+		gDeferredSkinnedDiffuseProgram.mName = "Deferred Skinned Diffuse Shader";
+		gDeferredSkinnedDiffuseProgram.mFeatures.hasObjectSkinning = true;
+		gDeferredSkinnedDiffuseProgram.mShaderFiles.clear();
+		gDeferredSkinnedDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredSkinnedDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredSkinnedDiffuseProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		success = gDeferredSkinnedDiffuseProgram.createShader(NULL, NULL);
+	}
+
+	if (success)
+	{
+		gDeferredSkinnedBumpProgram.mName = "Deferred Skinned Bump Shader";
+		gDeferredSkinnedBumpProgram.mFeatures.hasObjectSkinning = true;
+		gDeferredSkinnedBumpProgram.mShaderFiles.clear();
+		gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredSkinnedBumpProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		success = gDeferredSkinnedBumpProgram.createShader(NULL, NULL);
+	}
+
+	if (success)
+	{
+		gDeferredSkinnedAlphaProgram.mName = "Deferred Skinned Alpha Shader";
+		gDeferredSkinnedAlphaProgram.mFeatures.hasObjectSkinning = true;
+		gDeferredSkinnedAlphaProgram.mFeatures.calculatesLighting = true;
+		gDeferredSkinnedAlphaProgram.mFeatures.calculatesAtmospherics = true;
+		gDeferredSkinnedAlphaProgram.mFeatures.hasGamma = true;
+		gDeferredSkinnedAlphaProgram.mFeatures.hasAtmospherics = true;
+		gDeferredSkinnedAlphaProgram.mFeatures.hasLighting = true;
+		gDeferredSkinnedAlphaProgram.mShaderFiles.clear();
+		gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredSkinnedAlphaProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		success = gDeferredSkinnedAlphaProgram.createShader(NULL, NULL);
+	}
+
 	if (success)
 	{
 		gDeferredBumpProgram.mName = "Deferred Bump Shader";
@@ -959,7 +1051,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 	{
 		gDeferredMultiLightProgram.mName = "Deferred MultiLight Shader";
 		gDeferredMultiLightProgram.mShaderFiles.clear();
-		gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredMultiLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
 		success = gDeferredMultiLightProgram.createShader(NULL, NULL);
@@ -1065,7 +1157,14 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredSoftenProgram.mShaderFiles.clear();
 		gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+
 		gDeferredSoftenProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+
+		if (gSavedSettings.getBOOL("RenderDeferredSSAO"))
+		{ //if using SSAO, take screen space light map into account as if shadows are enabled
+			gDeferredSoftenProgram.mShaderLevel = llmax(gDeferredSoftenProgram.mShaderLevel, 2);
+		}
+				
 		success = gDeferredSoftenProgram.createShader(NULL, NULL);
 	}
 
@@ -1090,6 +1189,17 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		success = gDeferredAvatarShadowProgram.createShader(&mAvatarAttribs, &mAvatarUniforms);
 	}
 
+	if (success)
+	{
+		gDeferredAttachmentShadowProgram.mName = "Deferred Attachment Shadow Shader";
+		gDeferredAttachmentShadowProgram.mFeatures.hasObjectSkinning = true;
+		gDeferredAttachmentShadowProgram.mShaderFiles.clear();
+		gDeferredAttachmentShadowProgram.mShaderFiles.push_back(make_pair("deferred/attachmentShadowV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredAttachmentShadowProgram.mShaderFiles.push_back(make_pair("deferred/attachmentShadowF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredAttachmentShadowProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		success = gDeferredAttachmentShadowProgram.createShader(NULL, NULL);
+	}
+
 	if (success)
 	{
 		gTerrainProgram.mName = "Deferred Terrain Shader";
@@ -1122,11 +1232,31 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredAvatarAlphaProgram.mFeatures.hasLighting = true;
 		gDeferredAvatarAlphaProgram.mShaderFiles.clear();
 		gDeferredAvatarAlphaProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredAvatarAlphaProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredAvatarAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredAvatarAlphaProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
 		success = gDeferredAvatarAlphaProgram.createShader(&mAvatarAttribs, &mAvatarUniforms);
 	}
 
+	if (success)
+	{
+		gDeferredPostProgram.mName = "Deferred Post Shader";
+		gDeferredPostProgram.mShaderFiles.clear();
+		gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredPostProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		success = gDeferredPostProgram.createShader(NULL, NULL);
+	}
+
+	if (success)
+	{
+		gDeferredPostNoDoFProgram.mName = "Deferred Post Shader";
+		gDeferredPostNoDoFProgram.mShaderFiles.clear();
+		gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB));
+		gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoDoFF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gDeferredPostNoDoFProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		success = gDeferredPostNoDoFProgram.createShader(NULL, NULL);
+	}
+
 	if (mVertexShaderLevel[SHADER_DEFERRED] > 1)
 	{
 		if (success)
@@ -1142,15 +1272,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 
 	if (mVertexShaderLevel[SHADER_DEFERRED] > 2)
 	{
-		if (success)
-		{
-			gDeferredPostProgram.mName = "Deferred Post Shader";
-			gDeferredPostProgram.mShaderFiles.clear();
-			gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB));
-			gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gDeferredPostProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
-			success = gDeferredPostProgram.createShader(NULL, NULL);
-		}
+		
 
 		if (success)
 		{
@@ -1204,12 +1326,22 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 	{
 		gObjectShinyProgram.unload();
 		gObjectFullbrightShinyProgram.unload();
+		gObjectFullbrightShinyWaterProgram.unload();
 		gObjectShinyWaterProgram.unload();
 		gObjectSimpleProgram.unload();
 		gObjectSimpleWaterProgram.unload();
 		gObjectFullbrightProgram.unload();
 		gObjectFullbrightWaterProgram.unload();
-		return FALSE;
+		gSkinnedObjectSimpleProgram.unload();
+		gSkinnedObjectFullbrightProgram.unload();
+		gSkinnedObjectFullbrightShinyProgram.unload();
+		gSkinnedObjectShinySimpleProgram.unload();
+		gSkinnedObjectSimpleWaterProgram.unload();
+		gSkinnedObjectFullbrightWaterProgram.unload();
+		gSkinnedObjectFullbrightShinyWaterProgram.unload();
+		gSkinnedObjectShinySimpleWaterProgram.unload();
+	
+		return TRUE;
 	}
 
 	if (success)
@@ -1318,6 +1450,159 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		success = gObjectFullbrightShinyProgram.createShader(NULL, &mShinyUniforms);
 	}
 
+	if (success)
+	{
+		gObjectFullbrightShinyWaterProgram.mName = "Fullbright Shiny Water Shader";
+		gObjectFullbrightShinyWaterProgram.mFeatures.calculatesAtmospherics = true;
+		gObjectFullbrightShinyWaterProgram.mFeatures.isFullbright = true;
+		gObjectFullbrightShinyWaterProgram.mFeatures.isShiny = true;
+		gObjectFullbrightShinyWaterProgram.mFeatures.hasGamma = true;
+		gObjectFullbrightShinyWaterProgram.mFeatures.hasTransport = true;
+		gObjectFullbrightShinyWaterProgram.mFeatures.hasWaterFog = true;
+		gObjectFullbrightShinyWaterProgram.mShaderFiles.clear();
+		gObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyV.glsl", GL_VERTEX_SHADER_ARB));
+		gObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gObjectFullbrightShinyWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+		gObjectFullbrightShinyWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
+		success = gObjectFullbrightShinyWaterProgram.createShader(NULL, &mShinyUniforms);
+	}
+
+	if (mVertexShaderLevel[SHADER_AVATAR] > 0)
+	{ //load hardware skinned attachment shaders
+		if (success)
+		{
+			gSkinnedObjectSimpleProgram.mName = "Skinned Simple Shader";
+			gSkinnedObjectSimpleProgram.mFeatures.calculatesLighting = true;
+			gSkinnedObjectSimpleProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectSimpleProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectSimpleProgram.mFeatures.hasAtmospherics = true;
+			gSkinnedObjectSimpleProgram.mFeatures.hasLighting = true;
+			gSkinnedObjectSimpleProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectSimpleProgram.mShaderFiles.clear();
+			gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectSimpleProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectSimpleProgram.createShader(NULL, NULL);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectFullbrightProgram.mName = "Skinned Fullbright Shader";
+			gSkinnedObjectFullbrightProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectFullbrightProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectFullbrightProgram.mFeatures.hasTransport = true;
+			gSkinnedObjectFullbrightProgram.mFeatures.isFullbright = true;
+			gSkinnedObjectFullbrightProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectFullbrightProgram.mShaderFiles.clear();
+			gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectFullbrightProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectFullbrightProgram.createShader(NULL, NULL);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectFullbrightShinyProgram.mName = "Skinned Fullbright Shiny Shader";
+			gSkinnedObjectFullbrightShinyProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasTransport = true;
+			gSkinnedObjectFullbrightShinyProgram.mFeatures.isShiny = true;
+			gSkinnedObjectFullbrightShinyProgram.mFeatures.isFullbright = true;
+			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectFullbrightShinyProgram.mShaderFiles.clear();
+			gSkinnedObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectFullbrightShinyProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectFullbrightShinyProgram.createShader(NULL, &mShinyUniforms);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectShinySimpleProgram.mName = "Skinned Shiny Simple Shader";
+			gSkinnedObjectShinySimpleProgram.mFeatures.calculatesLighting = true;
+			gSkinnedObjectShinySimpleProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectShinySimpleProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectShinySimpleProgram.mFeatures.hasAtmospherics = true;
+			gSkinnedObjectShinySimpleProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectShinySimpleProgram.mFeatures.isShiny = true;
+			gSkinnedObjectShinySimpleProgram.mShaderFiles.clear();
+			gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinyF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectShinySimpleProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectShinySimpleProgram.createShader(NULL, &mShinyUniforms);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectSimpleWaterProgram.mName = "Skinned Simple Water Shader";
+			gSkinnedObjectSimpleWaterProgram.mFeatures.calculatesLighting = true;
+			gSkinnedObjectSimpleWaterProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectSimpleWaterProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectSimpleWaterProgram.mFeatures.hasAtmospherics = true;
+			gSkinnedObjectSimpleWaterProgram.mFeatures.hasLighting = true;
+			gSkinnedObjectSimpleWaterProgram.mFeatures.hasWaterFog = true;
+			gSkinnedObjectSimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
+			gSkinnedObjectSimpleWaterProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectSimpleWaterProgram.mShaderFiles.clear();
+			gSkinnedObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectSimpleWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectSimpleWaterProgram.createShader(NULL, NULL);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectFullbrightWaterProgram.mName = "Skinned Fullbright Water Shader";
+			gSkinnedObjectFullbrightWaterProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasTransport = true;
+			gSkinnedObjectFullbrightWaterProgram.mFeatures.isFullbright = true;
+			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasWaterFog = true;
+			gSkinnedObjectFullbrightWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
+			gSkinnedObjectFullbrightWaterProgram.mShaderFiles.clear();
+			gSkinnedObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectFullbrightWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectFullbrightWaterProgram.createShader(NULL, NULL);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectFullbrightShinyWaterProgram.mName = "Skinned Fullbright Shiny Water Shader";
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasTransport = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.isShiny = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.isFullbright = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasWaterFog = true;
+			gSkinnedObjectFullbrightShinyWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
+			gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.clear();
+			gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectFullbrightShinyWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectFullbrightShinyWaterProgram.createShader(NULL, &mShinyUniforms);
+		}
+
+		if (success)
+		{
+			gSkinnedObjectShinySimpleWaterProgram.mName = "Skinned Shiny Simple Water Shader";
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.calculatesLighting = true;
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.calculatesAtmospherics = true;
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasGamma = true;
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasAtmospherics = true;
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasObjectSkinning = true;
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.isShiny = true;
+			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasWaterFog = true;
+			gSkinnedObjectShinySimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
+			gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.clear();
+			gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+			gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/shinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
+			gSkinnedObjectShinySimpleWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+			success = gSkinnedObjectShinySimpleWaterProgram.createShader(NULL, &mShinyUniforms);
+		}
+	}
 
 	if( !success )
 	{
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index db880fded6ff5dce412325f67af363d3165c6c67..72ac5e02ee50d0931bfd595128732d201103f5d2 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -76,6 +76,7 @@ class LLViewerShaderMgr: public LLShaderMgr
 		MATERIAL_COLOR = 0,
 		SPECULAR_COLOR,
 		BINORMAL,
+		OBJECT_WEIGHT,
 		END_RESERVED_ATTRIBS
 	} eGLSLReservedAttribs;
 	
@@ -304,9 +305,20 @@ extern LLGLSLShader			gObjectSimpleLODProgram;
 extern LLGLSLShader			gObjectFullbrightLODProgram;
 
 extern LLGLSLShader			gObjectFullbrightShinyProgram;
+extern LLGLSLShader			gObjectFullbrightShinyWaterProgram;
 extern LLGLSLShader			gObjectShinyProgram;
 extern LLGLSLShader			gObjectShinyWaterProgram;
 
+extern LLGLSLShader			gSkinnedObjectSimpleProgram;
+extern LLGLSLShader			gSkinnedObjectFullbrightProgram;
+extern LLGLSLShader			gSkinnedObjectFullbrightShinyProgram;
+extern LLGLSLShader			gSkinnedObjectShinySimpleProgram;
+
+extern LLGLSLShader			gSkinnedObjectSimpleWaterProgram;
+extern LLGLSLShader			gSkinnedObjectFullbrightWaterProgram;
+extern LLGLSLShader			gSkinnedObjectFullbrightShinyWaterProgram;
+extern LLGLSLShader			gSkinnedObjectShinySimpleWaterProgram;
+
 //environment shaders
 extern LLGLSLShader			gTerrainProgram;
 extern LLGLSLShader			gTerrainWaterProgram;
@@ -337,6 +349,9 @@ extern LLGLSLShader			gDeferredImpostorProgram;
 extern LLGLSLShader			gDeferredEdgeProgram;
 extern LLGLSLShader			gDeferredWaterProgram;
 extern LLGLSLShader			gDeferredDiffuseProgram;
+extern LLGLSLShader			gDeferredSkinnedDiffuseProgram;
+extern LLGLSLShader			gDeferredSkinnedBumpProgram;
+extern LLGLSLShader			gDeferredSkinnedAlphaProgram;
 extern LLGLSLShader			gDeferredBumpProgram;
 extern LLGLSLShader			gDeferredTerrainProgram;
 extern LLGLSLShader			gDeferredTreeProgram;
@@ -353,7 +368,9 @@ extern LLGLSLShader			gDeferredSoftenProgram;
 extern LLGLSLShader			gDeferredShadowProgram;
 extern LLGLSLShader			gDeferredPostGIProgram;
 extern LLGLSLShader			gDeferredPostProgram;
+extern LLGLSLShader			gDeferredPostNoDoFProgram;
 extern LLGLSLShader			gDeferredAvatarShadowProgram;
+extern LLGLSLShader			gDeferredAttachmentShadowProgram;
 extern LLGLSLShader			gDeferredAlphaProgram;
 extern LLGLSLShader			gDeferredFullbrightProgram;
 extern LLGLSLShader			gDeferredAvatarAlphaProgram;
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index fa60e572ac44a268aed21aae4c200fbd807824ed..0fb94bc44bc9f52fc352dbf98427932ea286a4cf 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -59,6 +59,7 @@
 #include "llworld.h"
 #include "llfeaturemanager.h"
 #include "llviewernetwork.h"
+#include "llmeshrepository.h" //for LLMeshRepository::sBytesReceived
 
 
 class StatAttributes
@@ -794,6 +795,7 @@ void send_stats()
 	download["world_kbytes"] = gTotalWorldBytes / 1024.0;
 	download["object_kbytes"] = gTotalObjectBytes / 1024.0;
 	download["texture_kbytes"] = gTotalTextureBytes / 1024.0;
+	download["mesh_kbytes"] = LLMeshRepository::sBytesReceived/1024.0;
 
 	LLSD &in = body["stats"]["net"]["in"];
 
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 3f9cfb9d9b7c96f849146646f6d30b1f2e03ae84..f91a1241fe584cd5770bf009d19aa7ee67ff16f8 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -196,8 +196,15 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 		S32 mCount;
 		F32 mSum;
 		F32 mSumOfSquares;
+		F32 mMinValue;
+		F32 mMaxValue;
 		U32 mCountOfNextUpdatesToIgnore;
 
+		inline StatsAccumulator()
+		{
+			reset();
+		}
+
 		inline void push( F32 val )
 		{
 			if ( mCountOfNextUpdatesToIgnore > 0 )
@@ -209,13 +216,31 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 			mCount++;
 			mSum += val;
 			mSumOfSquares += val * val;
+			if (mCount == 1 || val > mMaxValue)
+			{
+				mMaxValue = val;
+			}
+			if (mCount == 1 || val < mMinValue)
+			{
+				mMinValue = val;
+			}
 		}
 		
 		inline F32 getMean() const
 		{
 			return (mCount == 0) ? 0.f : ((F32)mSum)/mCount;
 		}
-		
+
+		inline F32 getMinValue() const
+		{
+			return mMinValue;
+		}
+
+		inline F32 getMaxValue() const
+		{
+			return mMaxValue;
+		}
+
 		inline F32 getStdDev() const
 		{
 			const F32 mean = getMean();
@@ -231,6 +256,8 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 		{
 			mCount = 0;
 			mSum = mSumOfSquares = 0.f;
+			mMinValue = 0.0f;
+			mMaxValue = 0.0f;
 			mCountOfNextUpdatesToIgnore = 0;
 		}
 		
@@ -240,6 +267,8 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 			data["mean"] = getMean();
 			data["std_dev"] = getStdDev();
 			data["count"] = (S32)mCount;
+			data["min"] = getMinValue();
+			data["max"] = getMaxValue();
 			return data;
 		}
 	};
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
index 0c369708787b667b97cd56f4e0e0dfb516ca6a6d..4798bb536f1d4fe2824245ac56c789b7961be4f0 100644
--- a/indra/newview/llviewertexteditor.cpp
+++ b/indra/newview/llviewertexteditor.cpp
@@ -537,6 +537,7 @@ LLUIImagePtr LLEmbeddedItems::getItemImage(llwchar ext_char) const
 			case LLAssetType::AT_BODYPART:		img_name = "Inv_Skin";		break;
 			case LLAssetType::AT_ANIMATION:		img_name = "Inv_Animation";	break;
 			case LLAssetType::AT_GESTURE:		img_name = "Inv_Gesture";	break;
+			case LLAssetType::AT_MESH:          img_name = "Inv_Mesh";	    break;
 			default: llassert(0);
 		}
 
@@ -846,17 +847,18 @@ BOOL LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
 	{
 		switch( cargo_type )
 		{
-		case DAD_CALLINGCARD:
-		case DAD_TEXTURE:
-		case DAD_SOUND:
-		case DAD_LANDMARK:
-		case DAD_SCRIPT:
-		case DAD_CLOTHING:
-		case DAD_OBJECT:
-		case DAD_NOTECARD:
-		case DAD_BODYPART:
-		case DAD_ANIMATION:
-		case DAD_GESTURE:
+			case DAD_CALLINGCARD:
+			case DAD_TEXTURE:
+			case DAD_SOUND:
+			case DAD_LANDMARK:
+			case DAD_SCRIPT:
+			case DAD_CLOTHING:
+			case DAD_OBJECT:
+			case DAD_NOTECARD:
+			case DAD_BODYPART:
+			case DAD_ANIMATION:
+			case DAD_GESTURE:
+			case DAD_MESH:
 			{
 				LLInventoryItem *item = (LLInventoryItem *)cargo_data;
 				if( item && allowsEmbeddedItems() )
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index f5fb074992adce532399cc7f2d61dabd2a2e096f..af06421bf9db7c1d4686d739fc8df6d9879cc2c6 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -835,7 +835,7 @@ BOOL LLViewerTexture::createGLTexture(S32 discard_level, const LLImageRaw* image
 	llassert(mGLTexturep.notNull()) ;	
 
 	BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category) ;
-	
+
 	if(ret)
 	{
 		mFullWidth = mGLTexturep->getCurrentWidth() ;
@@ -1422,8 +1422,15 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
 		mOrigWidth = mRawImage->getWidth();
 		mOrigHeight = mRawImage->getHeight();
 
-		// leave black border, do not scale image content
-		mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE);
+			
+		if (mBoostLevel == BOOST_PREVIEW)
+		{ 
+			mRawImage->biasedScaleToPowerOfTwo(1024);
+		}
+		else
+		{ // leave black border, do not scale image content
+			mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE);
+		}
 		
 		mFullWidth = mRawImage->getWidth();
 		mFullHeight = mRawImage->getHeight();
@@ -1583,7 +1590,7 @@ F32 LLViewerFetchedTexture::calcDecodePriority()
 
 	S32 cur_discard = getCurrentDiscardLevelForFetching();
 	bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel));
-	F32 pixel_priority = fsqrtf(mMaxVirtualSize);
+	F32 pixel_priority = (F32) sqrt(mMaxVirtualSize);
 
 	F32 priority = 0.f;
 
@@ -2711,6 +2718,9 @@ void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard)
 }
 void LLViewerFetchedTexture::destroySavedRawImage()
 {
+	mForceToSaveRawImage  = FALSE ;
+	mSaveRawImage = FALSE ;
+
 	clearCallbackEntryList() ;
 	
 	mSavedRawImage = NULL ;
@@ -2870,7 +2880,7 @@ BOOL LLViewerFetchedTexture::insertToAtlas()
 	}
 
 	//process the waiting_list
-	for(ll_face_list_t::iterator iter = waiting_list.begin(); iter != waiting_list.end(); ++iter)
+	for(std::vector<LLFace*>::iterator iter = waiting_list.begin(); iter != waiting_list.end(); ++iter)
 	{
 		facep = (LLFace*)*iter ;	
 		groupp = facep->getDrawable()->getSpatialGroup() ;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 33e7328cd7396be534c84640e3929667d0119579..d9ff9315750592299c06f70973648550d15bf12d 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -835,7 +835,7 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
 	for (entries_list_t::iterator iter3 = entries.begin();
 		 iter3 != entries.end(); )
 	{
-		LLPointer<LLViewerFetchedTexture> imagep = *iter3++;
+		LLViewerFetchedTexture* imagep = *iter3++;
 		
 		bool fetching = imagep->updateFetch();
 		if (fetching)
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 7728958ed8090a3400c6abde6f5e768aeca93f2b..6fe79c2e8572c7130294c98e7c0df56b639beb12 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -25,7 +25,6 @@
  */
 
 #include "llviewerprecompiledheaders.h"
-
 #include "llviewerwindow.h"
 
 #if LL_WINDOWS
@@ -41,6 +40,7 @@
 #include "llagent.h"
 #include "llagentcamera.h"
 #include "llfloaterreg.h"
+#include "llmeshrepository.h"
 #include "llpanellogin.h"
 #include "llviewerkeyboard.h"
 #include "llviewermenu.h"
@@ -228,6 +228,8 @@ LLVector2       gDebugRaycastTexCoord;
 LLVector3       gDebugRaycastNormal;
 LLVector3       gDebugRaycastBinormal;
 S32				gDebugRaycastFaceHit;
+LLVector3		gDebugRaycastStart;
+LLVector3		gDebugRaycastEnd;
 
 // HUD display lines in lower right
 BOOL				gDisplayWindInfo = FALSE;
@@ -325,7 +327,7 @@ class LLDebugText
 		mTextColor = LLColor4( 0.86f, 0.86f, 0.86f, 1.f );
 
 		// Draw stuff growing up from right lower corner of screen
-		U32 xpos = mWindow->getWindowWidthScaled() - 350;
+		U32 xpos = mWindow->getWorldViewWidthScaled() - 350;
 		U32 ypos = 64;
 		const U32 y_inc = 20;
 
@@ -459,6 +461,79 @@ class LLDebugText
 				addText(xpos, ypos, "Shaders Disabled");
 				ypos += y_inc;
 			}
+
+			if (gGLManager.mHasATIMemInfo)
+			{
+				S32 meminfo[4];
+				glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
+
+				addText(xpos, ypos, llformat("%.2f MB Texture Memory Free", meminfo[0]/1024.f));
+				ypos += y_inc;
+
+				if (gGLManager.mHasVertexBufferObject)
+				{
+					glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, meminfo);
+					addText(xpos, ypos, llformat("%.2f MB VBO Memory Free", meminfo[0]/1024.f));
+					ypos += y_inc;
+				}
+			}
+			else if (gGLManager.mHasNVXMemInfo)
+			{
+				S32 free_memory;
+				glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory);
+				addText(xpos, ypos, llformat("%.2f MB Video Memory Free", free_memory/1024.f));
+				ypos += y_inc;
+			}
+
+			//show streaming cost/triangle count of known prims in current region OR selection
+			{
+				F32 cost = 0.f;
+				S32 count = 0;
+				S32 object_count = 0;
+				S32 total_bytes = 0;
+				S32 visible_bytes = 0;
+
+				const char* label = "Region";
+				if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 0)
+				{ //region
+					LLViewerRegion* region = gAgent.getRegion();
+					if (region)
+					{
+						for (U32 i = 0; i < gObjectList.getNumObjects(); ++i)
+						{
+							LLViewerObject* object = gObjectList.getObject(i);
+							if (object && 
+								object->getRegion() == region &&
+								object->getVolume())
+							{
+								object_count++;
+								S32 bytes = 0;	
+								S32 visible = 0;
+								cost += object->getStreamingCost(&bytes, &visible);
+								count += object->getTriangleCount();
+								total_bytes += bytes;
+								visible_bytes += visible;
+							}
+						}
+					}
+				}
+				else
+				{
+					label = "Selection";
+					cost = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectStreamingCost(&total_bytes, &visible_bytes);
+					count = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectTriangleCount();
+					object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
+				}
+					
+				addText(xpos,ypos, llformat("%s streaming cost: %.1f", label, cost));
+				ypos += y_inc;
+
+				addText(xpos, ypos, llformat("    %.1f KTris, %.1f/%.1f KB, %d objects",
+										count/1024.f, visible_bytes/1024.f, total_bytes/1024.f, object_count));
+				ypos += y_inc;
+			
+			}
+
 			addText(xpos, ypos, llformat("%d MB Vertex Data", LLVertexBuffer::sAllocatedBytes/(1024*1024)));
 			ypos += y_inc;
 
@@ -511,6 +586,12 @@ class LLDebugText
 			
 			ypos += y_inc;
 
+			if (!LLSpatialGroup::sPendingQueries.empty())
+			{
+				addText(xpos,ypos, llformat("%d Queries pending", LLSpatialGroup::sPendingQueries.size()));
+				ypos += y_inc;
+			}
+
 
 			addText(xpos,ypos, llformat("%d Avatars visible", LLVOAvatar::sNumVisibleAvatars));
 			
@@ -520,6 +601,22 @@ class LLDebugText
 			
 			ypos += y_inc;
 
+			if (gSavedSettings.getBOOL("MeshEnabled"))
+			{
+				addText(xpos, ypos, llformat("%.3f MB Mesh Data Received", LLMeshRepository::sBytesReceived/(1024.f*1024.f)));
+				
+				ypos += y_inc;
+				
+				addText(xpos, ypos, llformat("%d/%d Mesh HTTP Requests/Retries", LLMeshRepository::sHTTPRequestCount,
+					LLMeshRepository::sHTTPRetryCount));
+				
+				ypos += y_inc;
+
+				addText(xpos, ypos, llformat("%.3f/%.3f MB Mesh Cache Read/Write ", LLMeshRepository::sCacheBytesRead/(1024.f*1024.f), LLMeshRepository::sCacheBytesWritten/(1024.f*1024.f)));
+
+				ypos += y_inc;
+			}
+
 			LLVertexBuffer::sBindCount = LLImageGL::sBindCount = 
 				LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount = 
 				gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0;
@@ -610,6 +707,7 @@ class LLDebugText
 				ypos += y_inc;
 			}
 		}
+
 		if(log_texture_traffic)
 		{	
 			U32 old_y = ypos ;
@@ -626,6 +724,52 @@ class LLDebugText
 				addText(xpos, ypos, "Network traffic for textures:");
 				ypos += y_inc;
 			}
+		}				
+
+		if (gSavedSettings.getBOOL("DebugShowUploadCost"))
+		{
+			addText(xpos, ypos, llformat("       Meshes: L$%d", gPipeline.mDebugMeshUploadCost));
+			ypos += y_inc/2;
+			addText(xpos, ypos, llformat("    Sculpties: L$%d", gPipeline.mDebugSculptUploadCost));
+			ypos += y_inc/2;
+			addText(xpos, ypos, llformat("     Textures: L$%d", gPipeline.mDebugTextureUploadCost));
+			ypos += y_inc/2;
+			addText(xpos, ypos, "Upload Cost: ");
+						
+			ypos += y_inc;
+		}
+
+		//temporary hack to give feedback on mesh upload progress
+		if (!gMeshRepo.mUploads.empty())
+		{
+			for (std::vector<LLMeshUploadThread*>::iterator iter = gMeshRepo.mUploads.begin(); 
+				iter != gMeshRepo.mUploads.end(); ++iter)
+			{
+				LLMeshUploadThread* thread = *iter;
+
+				addText(xpos, ypos, llformat("Mesh Upload -- price quote: %d:%d | upload: %d:%d | create: %d", 
+								thread->mPendingConfirmations, thread->mUploadQ.size()+thread->mTextureQ.size(),
+								thread->mPendingUploads, thread->mConfirmedQ.size()+thread->mConfirmedTextureQ.size(),
+								thread->mInstanceQ.size()));
+				ypos += y_inc;
+			}
+		}
+
+		if (!gMeshRepo.mPendingRequests.empty() ||
+			!gMeshRepo.mThread->mHeaderReqQ.empty() ||
+			!gMeshRepo.mThread->mLODReqQ.empty())
+		{
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+			S32 pending = (S32) gMeshRepo.mPendingRequests.size();
+			S32 header = (S32) gMeshRepo.mThread->mHeaderReqQ.size();
+			S32 lod = (S32) gMeshRepo.mThread->mLODReqQ.size();
+
+			addText(xpos, ypos, llformat ("Mesh Queue - %d pending (%d:%d header | %d:%d LOD)", 
+												pending,
+												LLMeshRepoThread::sActiveHeaderRequests, header,
+												LLMeshRepoThread::sActiveLODRequests, lod));
+
+			ypos += y_inc;
 		}
 		
 		if (gSavedSettings.getBOOL("DebugShowTextureInfo"))
@@ -1432,7 +1576,7 @@ LLViewerWindow::LLViewerWindow(
 		gSavedSettings.getBOOL("DisableVerticalSync"),
 		!gHeadlessClient,
 		ignore_pixel_depth,
-		gSavedSettings.getBOOL("RenderUseFBO") ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled
+		gSavedSettings.getBOOL("RenderDeferred") ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled
 
 	if (!LLAppViewer::instance()->restoreErrorTrap())
 	{
@@ -2604,7 +2748,9 @@ void LLViewerWindow::updateUI()
 											  &gDebugRaycastIntersection,
 											  &gDebugRaycastTexCoord,
 											  &gDebugRaycastNormal,
-											  &gDebugRaycastBinormal);
+											  &gDebugRaycastBinormal,
+											  &gDebugRaycastStart,
+											  &gDebugRaycastEnd);
 	}
 
 	updateMouseDelta();
@@ -3539,7 +3685,9 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 												LLVector3 *intersection,
 												LLVector2 *uv,
 												LLVector3 *normal,
-												LLVector3 *binormal)
+												LLVector3 *binormal,
+												LLVector3* start,
+												LLVector3* end)
 {
 	S32 x = mouse_x;
 	S32 y = mouse_y;
@@ -3571,7 +3719,22 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 	LLVector3 mouse_world_start = mouse_point_global;
 	LLVector3 mouse_world_end   = mouse_point_global + mouse_direction_global * depth;
 
-	
+	if (!LLViewerJoystick::getInstance()->getOverrideCamera())
+	{ //always set raycast intersection to mouse_world_end unless
+		//flycam is on (for DoF effect)
+		gDebugRaycastIntersection = mouse_world_end;
+	}
+
+	if (start)
+	{
+		*start = mouse_world_start;
+	}
+
+	if (end)
+	{
+		*end = mouse_world_end;
+	}
+
 	LLViewerObject* found = NULL;
 
 	if (this_object)  // check only this object
@@ -3583,8 +3746,7 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 			{
 				found = this_object;
 			}
-			}
-		
+		}
 		else // is a world object
 		{
 			if (this_object->lineSegmentIntersect(mouse_world_start, mouse_world_end, this_face, pick_transparent,
@@ -3592,21 +3754,22 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 			{
 				found = this_object;
 			}
-			}
-			}
-
+		}
+	}
 	else // check ALL objects
-			{
+	{
 		found = gPipeline.lineSegmentIntersectInHUD(mouse_hud_start, mouse_hud_end, pick_transparent,
 													face_hit, intersection, uv, normal, binormal);
 
 		if (!found) // if not found in HUD, look in world:
-
-			{
+		{
 			found = gPipeline.lineSegmentIntersectInWorld(mouse_world_start, mouse_world_end, pick_transparent,
 														  face_hit, intersection, uv, normal, binormal);
+			if (found && !pick_transparent)
+			{
+				gDebugRaycastIntersection = *intersection;
 			}
-
+		}
 	}
 
 	return found;
@@ -4573,6 +4736,7 @@ BOOL LLViewerWindow::changeDisplaySettings(LLCoordScreen size, BOOL disable_vsyn
 
 	//gResizeScreenTexture = TRUE;
 
+
 	//U32 fsaa = gSavedSettings.getU32("RenderFSAASamples");
 	//U32 old_fsaa = mWindow->getFSAASamples();
 
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index bb0023b787a3b80400b180993f287fd2c66823c2..df6928aa1d0ada833ff98edcdb71d111cf220e13 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -346,7 +346,9 @@ class LLViewerWindow : public LLWindowCallbacks
 									LLVector3 *intersection = NULL,
 									LLVector2 *uv = NULL,
 									LLVector3 *normal = NULL,
-									LLVector3 *binormal = NULL);
+									LLVector3 *binormal = NULL,
+									LLVector3* start = NULL,
+									LLVector3* end = NULL);
 	
 	
 	// Returns a pointer to the last object hit
@@ -480,6 +482,8 @@ extern LLVector2        gDebugRaycastTexCoord;
 extern LLVector3        gDebugRaycastNormal;
 extern LLVector3        gDebugRaycastBinormal;
 extern S32				gDebugRaycastFaceHit;
+extern LLVector3		gDebugRaycastStart;
+extern LLVector3		gDebugRaycastEnd;
 
 extern BOOL			gDisplayCameraPos;
 extern BOOL			gDisplayWindInfo;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 79866dc5d2bdda90cef4d3b719979e0afb5ae4aa..ec2b5a4c9800cc7ac7bd3711bc7984971d04151a 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -64,6 +64,8 @@
 #include "llkeyframefallmotion.h"
 #include "llkeyframestandmotion.h"
 #include "llkeyframewalkmotion.h"
+#include "llmanipscale.h"  // for get_default_max_prim_scale()
+#include "llmeshrepository.h"
 #include "llmutelist.h"
 #include "llmoveview.h"
 #include "llnotificationsutil.h"
@@ -81,6 +83,7 @@
 #include "llviewermenu.h"
 #include "llviewerobjectlist.h"
 #include "llviewerparcelmgr.h"
+#include "llviewershadermgr.h"
 #include "llviewerstats.h"
 #include "llvoavatarself.h"
 #include "llvovolume.h"
@@ -679,12 +682,14 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mTexHairColor( NULL ),
 	mTexEyeColor( NULL ),
 	mNeedsSkin(FALSE),
+	mLastSkinTime(0.f),
 	mUpdatePeriod(1),
 	mFullyLoaded(FALSE),
 	mPreviousFullyLoaded(FALSE),
 	mFullyLoadedInitialized(FALSE),
 	mSupportsAlphaLayers(FALSE),
-	mLoadedCallbacksPaused(FALSE)
+	mLoadedCallbacksPaused(FALSE),
+	mHasPelvisOffset( FALSE )
 {
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 	//VTResume();  // VTune
@@ -759,6 +764,10 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mRuthTimer.reset();
 	mRuthDebugTimer.reset();
 	mDebugExistenceTimer.reset();
+	mPelvisOffset = LLVector3(0.0f,0.0f,0.0f);
+	mLastPelvisToFoot = 0.0f;
+	mPelvisFixup = 0.0f;
+	mLastPelvisFixup = 0.0f;
 }
 
 //------------------------------------------------------------------------
@@ -1322,7 +1331,17 @@ const LLVector3 LLVOAvatar::getRenderPosition() const
 	}
 	else if (isRoot())
 	{
-		return mDrawable->getPositionAgent();
+		if ( !mHasPelvisOffset )
+		{
+			return mDrawable->getPositionAgent();
+		}
+		else
+		{
+			//Apply a pelvis fixup (as defined by the avs skin)
+			LLVector3 pos = mDrawable->getPositionAgent();
+			pos[VZ] += mPelvisFixup;
+			return pos;
+		}
 	}
 	else
 	{
@@ -1335,42 +1354,47 @@ void LLVOAvatar::updateDrawable(BOOL force_damped)
 	clearChanged(SHIFTED);
 }
 
-void LLVOAvatar::onShift(const LLVector3& shift_vector)
+void LLVOAvatar::onShift(const LLVector4a& shift_vector)
 {
-	mLastAnimExtents[0] += shift_vector;
-	mLastAnimExtents[1] += shift_vector;
+	const LLVector3& shift = reinterpret_cast<const LLVector3&>(shift_vector);
+	mLastAnimExtents[0] += shift;
+	mLastAnimExtents[1] += shift;
 	mNeedsImpostorUpdate = TRUE;
 	mNeedsAnimUpdate = TRUE;
 }
 
-void LLVOAvatar::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
 {
 	if (isImpostor() && !needsImpostorUpdate())
 	{
 		LLVector3 delta = getRenderPosition() -
-			((LLVector3(mDrawable->getPositionGroup())-mImpostorOffset));
+			((LLVector3(mDrawable->getPositionGroup().getF32ptr())-mImpostorOffset));
 		
-		newMin = mLastAnimExtents[0] + delta;
-		newMax = mLastAnimExtents[1] + delta;
+		newMin.load3( (mLastAnimExtents[0] + delta).mV);
+		newMax.load3( (mLastAnimExtents[1] + delta).mV);
 	}
 	else
 	{
 		getSpatialExtents(newMin,newMax);
-		mLastAnimExtents[0] = newMin;
-		mLastAnimExtents[1] = newMax;
-		LLVector3 pos_group = (newMin+newMax)*0.5f;
-		mImpostorOffset = pos_group-getRenderPosition();
+		mLastAnimExtents[0].set(newMin.getF32ptr());
+		mLastAnimExtents[1].set(newMax.getF32ptr());
+		LLVector4a pos_group;
+		pos_group.setAdd(newMin,newMax);
+		pos_group.mul(0.5f);
+		mImpostorOffset = LLVector3(pos_group.getF32ptr())-getRenderPosition();
 		mDrawable->setPositionGroup(pos_group);
 	}
 }
 
-void LLVOAvatar::getSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {
-	LLVector3 buffer(0.25f, 0.25f, 0.25f);
-	LLVector3 pos = getRenderPosition();
-	newMin = pos - buffer;
-	newMax = pos + buffer;
-	float max_attachment_span = DEFAULT_MAX_PRIM_SCALE * 5.0f;
+	LLVector4a buffer(0.25f);
+	LLVector4a pos;
+	pos.load3(getRenderPosition().mV);
+	newMin.setSub(pos, buffer);
+	newMax.setAdd(pos, buffer);
+
+	float max_attachment_span = get_default_max_prim_scale() * 5.0f;
 	
 	//stretch bounding box by joint positions
 	for (polymesh_map_t::iterator i = mMeshes.begin(); i != mMeshes.end(); ++i)
@@ -1378,12 +1402,20 @@ void LLVOAvatar::getSpatialExtents(LLVector3& newMin, LLVector3& newMax)
 		LLPolyMesh* mesh = i->second;
 		for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.count(); joint_num++)
 		{
-			update_min_max(newMin, newMax, 
-						   mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation());
+			LLVector4a trans;
+			trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV);
+			update_min_max(newMin, newMax, trans);
 		}
 	}
 
-	mPixelArea = LLPipeline::calcPixelArea((newMin+newMax)*0.5f, (newMax-newMin)*0.5f, *LLViewerCamera::getInstance());
+	LLVector4a center, size;
+	center.setAdd(newMin, newMax);
+	center.mul(0.5f);
+
+	size.setSub(newMax,newMin);
+	size.mul(0.5f);
+
+	mPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());
 
 	//stretch bounding box by attachments
 	for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); 
@@ -1405,20 +1437,22 @@ void LLVOAvatar::getSpatialExtents(LLVector3& newMin, LLVector3& newMax)
 			if (attached_object && !attached_object->isHUDAttachment())
 			{
 				LLDrawable* drawable = attached_object->mDrawable;
-				if (drawable)
+				if (drawable && !drawable->isState(LLDrawable::RIGGED))
 				{
 					LLSpatialBridge* bridge = drawable->getSpatialBridge();
 					if (bridge)
 					{
-						const LLVector3* ext = bridge->getSpatialExtents();
-						LLVector3 distance = (ext[1] - ext[0]);
+						const LLVector4a* ext = bridge->getSpatialExtents();
+						LLVector4a distance;
+						distance.setSub(ext[1], ext[0]);
+						LLVector4a max_span(max_attachment_span);
+
+						S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7;
 						
 						// Only add the prim to spatial extents calculations if it isn't a megaprim.
 						// max_attachment_span calculated at the start of the function 
 						// (currently 5 times our max prim size) 
-						if (distance.mV[0] < max_attachment_span 
-							&& distance.mV[1] < max_attachment_span
-							&& distance.mV[2] < max_attachment_span)
+						if (lt == 0x7)
 						{
 							update_min_max(newMin,newMax,ext[0]);
 							update_min_max(newMin,newMax,ext[1]);
@@ -1430,8 +1464,9 @@ void LLVOAvatar::getSpatialExtents(LLVector3& newMin, LLVector3& newMax)
 	}
 
 	//pad bounding box	
-	newMin -= buffer;
-	newMax += buffer;
+
+	newMin.sub(buffer);
+	newMax.add(buffer);
 }
 
 //-----------------------------------------------------------------------------
@@ -1606,7 +1641,8 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent
 							 info->mRot.mV[VZ], LLQuaternion::XYZ));
 	joint->setScale(info->mScale);
 
-
+	joint->setDefaultFromCurrentXform();
+	
 	if (info->mIsJoint)
 	{
 		joint->setSkinOffset( info->mPivot );
@@ -1984,26 +2020,29 @@ void LLVOAvatar::updateMeshData()
 
 			bool terse_update = false;
 
-			if(facep->mVertexBuffer.isNull())
+			facep->setGeomIndex(0);
+			facep->setIndicesIndex(0);
+		
+			LLVertexBuffer* buff = facep->getVertexBuffer();
+			if(!facep->getVertexBuffer())
 			{
-				facep->mVertexBuffer = new LLVertexBufferAvatar();
-				facep->mVertexBuffer->allocateBuffer(num_vertices, num_indices, TRUE);
+				buff = new LLVertexBufferAvatar();
+				buff->allocateBuffer(num_vertices, num_indices, TRUE);
+				facep->setVertexBuffer(buff);
 			}
 			else
 			{
-				if (facep->mVertexBuffer->getRequestedIndices() == num_indices &&
-					facep->mVertexBuffer->getRequestedVerts() == num_vertices)
+				if (buff->getRequestedIndices() == num_indices &&
+					buff->getRequestedVerts() == num_vertices)
 				{
 					terse_update = true;
 				}
 				else
 				{
-				facep->mVertexBuffer->resizeBuffer(num_vertices, num_indices) ;
-			}
+					buff->resizeBuffer(num_vertices, num_indices);
+				}
 			}
-		
-			facep->setGeomIndex(0);
-			facep->setIndicesIndex(0);
+			
 		
 			// This is a hack! Avatars have their own pool, so we are detecting
 			//   the case of more than one avatar in the pool (thus > 0 instead of >= 0)
@@ -2018,7 +2057,7 @@ void LLVOAvatar::updateMeshData()
 			}
 
 			stop_glerror();
-			facep->mVertexBuffer->setBuffer(0);
+			buff->setBuffer(0);
 
 			if(!f_num)
 			{
@@ -2386,9 +2425,19 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
 		// here we get the approximate head position and set as sound source for the voice symbol
 		// (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
 		//--------------------------------------------------------------------------------------------
-		LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
-		mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset );
 		
+		if ( mIsSitting )
+		{
+			LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
+			mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset );
+		}
+		else 
+		{
+			LLVector3 tagPos = mRoot.getWorldPosition();
+			tagPos[VZ] -= mPelvisToFoot;
+			tagPos[VZ] += ( mBodySize[VZ] + 0.125f );
+			mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos );
+		}
 	}//if ( voiceEnabled )
 }		
 
@@ -2452,7 +2501,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)
 
 	if (isImpostor() && !mNeedsImpostorUpdate)
 	{
-		LLVector3 ext[2];
+		LLVector4a ext[2];
 		F32 distance;
 		LLVector3 angle;
 
@@ -2481,12 +2530,22 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)
 			}
 			else
 			{
+				//VECTORIZE THIS
 				getSpatialExtents(ext[0], ext[1]);
-				if ((ext[1]-mImpostorExtents[1]).length() > 0.05f ||
-					(ext[0]-mImpostorExtents[0]).length() > 0.05f)
+				LLVector4a diff;
+				diff.setSub(ext[1], mImpostorExtents[1]);
+				if (diff.getLength3().getF32() > 0.05f)
 				{
 					mNeedsImpostorUpdate = TRUE;
 				}
+				else
+				{
+					diff.setSub(ext[0], mImpostorExtents[0]);
+					if (diff.getLength3().getF32() > 0.05f)
+					{
+						mNeedsImpostorUpdate = TRUE;
+					}
+				}
 			}
 		}
 	}
@@ -2825,10 +2884,8 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 				}
 				
 	LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last);
-	mNameText->setPositionAgent(name_position);
-				
-	idleUpdateNameTagText(new_name);
-			
+	mNameText->setPositionAgent(name_position);				
+	idleUpdateNameTagText(new_name);			
 	idleUpdateNameTagAlpha(new_name, alpha);
 }
 
@@ -3114,8 +3171,9 @@ void LLVOAvatar::invalidateNameTags()
 		if (avatar->isDead()) continue;
 
 		avatar->clearNameTag();
-				}
-			}
+
+	}
+}
 
 // Compute name tag position during idle update
 LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last)
@@ -3134,12 +3192,14 @@ LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last)
 	local_camera_up.scaleVec(mBodySize * 0.5f);
 	local_camera_at.scaleVec(mBodySize * 0.5f);
 
-	LLVector3 name_position = mRoot.getWorldPosition() + 
-		(local_camera_up * root_rot) -
-		(projected_vec(local_camera_at * root_rot, camera_to_av));
+	LLVector3 name_position = mRoot.getWorldPosition();
+	name_position[VZ] -= mPelvisToFoot;
+	name_position[VZ] += (mBodySize[VZ]* 0.55f);
+	name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av));	
 	name_position += pixel_up_vec * 15.f;
+
 	return name_position;
-		}
+}
 
 void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha)
 {
@@ -3260,20 +3320,28 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 		mTimeVisible.reset();
 	}
 
-
+	
 	//--------------------------------------------------------------------
 	// the rest should only be done occasionally for far away avatars
 	//--------------------------------------------------------------------
 
 	if (visible && !isSelf() && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter)
 	{
+		const LLVector4a* ext = mDrawable->getSpatialExtents();
+		LLVector4a size;
+		size.setSub(ext[1],ext[0]);
+		F32 mag = size.getLength3().getF32()*0.5f;
+
 		F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f);
 		if (LLMuteList::getInstance()->isMuted(getID()))
 		{ // muted avatars update at 16 hz
 			mUpdatePeriod = 16;
 		}
-		else if (mVisibilityRank <= LLVOAvatar::sMaxVisible)
+		else if (mVisibilityRank <= LLVOAvatar::sMaxVisible ||
+			mDrawable->mDistanceWRTCamera < 1.f + mag)
 		{ //first 25% of max visible avatars are not impostored
+			//also, don't impostor avatars whose bounding box may be penetrating the 
+			//impostor camera near clip plane
 			mUpdatePeriod = 1;
 		}
 		else if (mVisibilityRank > LLVOAvatar::sMaxVisible * 4)
@@ -3384,7 +3452,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 		root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());
 
 		resolveHeightGlobal(root_pos, ground_under_pelvis, normal);
-		F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);
+		F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);				
 		BOOL in_air = ((!LLWorld::getInstance()->getRegionFromPosGlobal(ground_under_pelvis)) || 
 						foot_to_ground > FOOT_GROUND_COLLISION_TOLERANCE);
 
@@ -3398,13 +3466,12 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 		// of the agent's physical representation
 		root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot;
 		
-
 		LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos);
 
 		if (newPosition != mRoot.getXform()->getWorldPosition())
 		{		
 			mRoot.touch();
-			mRoot.setWorldPosition(newPosition ); // regular update
+			mRoot.setWorldPosition( newPosition ); // regular update				
 		}
 
 
@@ -3680,7 +3747,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 
 	return TRUE;
 }
-
 //-----------------------------------------------------------------------------
 // updateHeadOffset()
 //-----------------------------------------------------------------------------
@@ -3705,7 +3771,41 @@ void LLVOAvatar::updateHeadOffset()
 		mHeadOffset = lerp(midEyePt, mHeadOffset,  u);
 	}
 }
-
+//------------------------------------------------------------------------
+// setPelvisOffset
+//------------------------------------------------------------------------
+void LLVOAvatar::setPelvisOffset( bool hasOffset, const LLVector3& offsetAmount, F32 pelvisFixup ) 
+{
+	mHasPelvisOffset = hasOffset;
+	if ( mHasPelvisOffset )
+	{
+		//Store off last pelvis to foot value
+		mLastPelvisToFoot = mPelvisToFoot;
+		mPelvisOffset	  = offsetAmount;
+		mLastPelvisFixup  = mPelvisFixup;
+		mPelvisFixup	  = pelvisFixup;
+	}
+}
+//------------------------------------------------------------------------
+// postPelvisSetRecalc
+//------------------------------------------------------------------------
+void LLVOAvatar::postPelvisSetRecalc( void )
+{	
+	computeBodySize(); 
+	mRoot.touch();
+	mRoot.updateWorldMatrixChildren();	
+	dirtyMesh();
+	updateHeadOffset();
+}
+//------------------------------------------------------------------------
+// pelisPoke
+//------------------------------------------------------------------------
+void LLVOAvatar::setPelvisOffset( F32 pelvisFixupAmount )
+{	
+	mHasPelvisOffset  = true;
+	mLastPelvisFixup  = mPelvisFixup;	
+	mPelvisFixup	  = pelvisFixupAmount;	
+}
 //------------------------------------------------------------------------
 // updateVisibility()
 //------------------------------------------------------------------------
@@ -3855,6 +3955,46 @@ bool LLVOAvatar::shouldAlphaMask()
 
 }
 
+U32 LLVOAvatar::renderSkinnedAttachments()
+{
+	/*U32 num_indices = 0;
+	
+	const U32 data_mask =	LLVertexBuffer::MAP_VERTEX | 
+							LLVertexBuffer::MAP_NORMAL | 
+							LLVertexBuffer::MAP_TEXCOORD0 |
+							LLVertexBuffer::MAP_COLOR |
+							LLVertexBuffer::MAP_WEIGHT4;
+
+	for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
+		 iter != mAttachmentPoints.end();
+		 ++iter)
+	{
+		LLViewerJointAttachment* attachment = iter->second;
+		for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+			 attachment_iter != attachment->mAttachedObjects.end();
+			 ++attachment_iter)
+		{
+			const LLViewerObject* attached_object = (*attachment_iter);
+			if (attached_object && !attached_object->isHUDAttachment())
+			{
+				const LLDrawable* drawable = attached_object->mDrawable;
+				if (drawable)
+				{
+					for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+					{
+						LLFace* face = drawable->getFace(i);
+						if (face->isState(LLFace::RIGGED))
+						{
+							
+				}
+			}
+		}
+	}
+
+	return num_indices;*/
+	return 0;
+}
+
 //-----------------------------------------------------------------------------
 // renderSkinned()
 //-----------------------------------------------------------------------------
@@ -3869,7 +4009,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 
 	LLFace* face = mDrawable->getFace(0);
 
-	bool needs_rebuild = !face || face->mVertexBuffer.isNull() || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY);
+	bool needs_rebuild = !face || !face->getVertexBuffer() || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY);
 
 	if (needs_rebuild || mDirtyMesh)
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
@@ -3902,8 +4042,9 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 				mMeshLOD[MESH_ID_HAIR]->updateJointGeometry();
 			}
 			mNeedsSkin = FALSE;
-			
-			LLVertexBuffer* vb = mDrawable->getFace(0)->mVertexBuffer;
+			mLastSkinTime = gFrameTimeSeconds;
+
+			LLVertexBuffer* vb = mDrawable->getFace(0)->getVertexBuffer();
 			if (vb)
 			{
 				vb->setBuffer(0);
@@ -4250,7 +4391,7 @@ void LLVOAvatar::updateTextures()
 
 	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 	{
-		setDebugText(llformat("%4.0f:%4.0f", fsqrtf(mMinPixelArea),fsqrtf(mMaxPixelArea)));
+		setDebugText(llformat("%4.0f:%4.0f", (F32) sqrt(mMinPixelArea),(F32) sqrt(mMaxPixelArea)));
 	}	
 }
 
@@ -4392,7 +4533,6 @@ void LLVOAvatar::resolveRayCollisionAgent(const LLVector3d start_pt, const LLVec
 	LLWorld::getInstance()->resolveStepHeightGlobal(this, start_pt, end_pt, out_pos, out_norm, &obj);
 }
 
-
 void LLVOAvatar::resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm)
 {
 	LLVector3d zVec(0.0f, 0.0f, 0.5f);
@@ -4788,6 +4928,80 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name )
 	return jointp;
 }
 
+//-----------------------------------------------------------------------------
+// resetJointPositions
+//-----------------------------------------------------------------------------
+void LLVOAvatar::resetJointPositions( void )
+{
+	for(S32 i = 0; i < (S32)mNumJoints; ++i)
+	{
+		mSkeleton[i].restoreOldXform();
+		mSkeleton[i].setId( LLUUID::null );
+	}
+	mHasPelvisOffset = false;
+	mPelvisFixup	 = mLastPelvisFixup;
+}
+//-----------------------------------------------------------------------------
+// resetSpecificJointPosition
+//-----------------------------------------------------------------------------
+void LLVOAvatar::resetSpecificJointPosition( const std::string& name )
+{
+	LLJoint* pJoint = mRoot.findJoint( name );
+	
+	if ( pJoint  && pJoint->doesJointNeedToBeReset() )
+	{
+		pJoint->restoreOldXform();
+		pJoint->setId( LLUUID::null );
+		//If we're reseting the pelvis position make sure not to apply offset
+		if ( name == "mPelvis" )
+		{
+			mHasPelvisOffset = false;
+		}
+	}
+	else
+	{
+		llinfos<<"Did not find "<< name.c_str()<<llendl;
+	}
+}
+//-----------------------------------------------------------------------------
+// resetJointPositionsToDefault
+//-----------------------------------------------------------------------------
+void LLVOAvatar::resetJointPositionsToDefault( void )
+{
+	const LLVector3& avPos = getCharacterPosition();
+	
+	//Reposition the pelvis
+	LLJoint* pPelvis = mRoot.findJoint("mPelvis");
+	if ( pPelvis )
+	{
+		pPelvis->setPosition( avPos + pPelvis->getPosition() );
+	}
+	else 
+	{
+		llwarns<<"Can't get pelvis joint."<<llendl;	
+		return;
+	}
+
+	//Subsequent joints are relative to pelvis
+	for( S32 i = 0; i < (S32)mNumJoints; ++i )
+	{
+		LLJoint* pJoint = (LLJoint*)&mSkeleton[i];
+		if ( pJoint->doesJointNeedToBeReset() )
+		{
+
+			pJoint->setId( LLUUID::null );
+			//restore joints to default positions, however skip over the pelvis
+			if ( pJoint && pPelvis != pJoint )
+			{
+				pJoint->restoreOldXform();
+			}
+		}
+	}
+	//make sure we don't apply the joint offset
+	mHasPelvisOffset = false;
+	mPelvisFixup	 = mLastPelvisFixup;
+	postPelvisSetRecalc();
+}
 //-----------------------------------------------------------------------------
 // getCharacterPosition()
 //-----------------------------------------------------------------------------
@@ -5445,9 +5659,13 @@ void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
 		return;
 	}
 
-	const LLVector3* ext = mDrawable->getSpatialExtents();
-	LLVector3 center = (ext[1] + ext[0]) * 0.5f;
-	LLVector3 size = (ext[1]-ext[0])*0.5f;
+	const LLVector4a* ext = mDrawable->getSpatialExtents();
+	LLVector4a center;
+	center.setAdd(ext[1], ext[0]);
+	center.mul(0.5f);
+	LLVector4a size;
+	size.setSub(ext[1], ext[0]);
+	size.mul(0.5f);
 
 	mImpostorPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());
 
@@ -5459,7 +5677,7 @@ void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
 	}
 	else
 	{
-		F32 radius = size.length();
+		F32 radius = size.getLength3().getF32();
 		mAppAngle = (F32) atan2( radius, range) * RAD_TO_DEG;
 	}
 
@@ -5770,6 +5988,52 @@ void LLVOAvatar::resetHUDAttachments()
 	}
 }
 
+void LLVOAvatar::rebuildRiggedAttachments( void )
+{
+	for ( attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter )
+	{
+		LLViewerJointAttachment* pAttachment = iter->second;
+		LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIterEnd = pAttachment->mAttachedObjects.end();
+		
+		for ( LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIter = pAttachment->mAttachedObjects.begin();
+			 attachmentIter != attachmentIterEnd; ++attachmentIter)
+		{
+			const LLViewerObject* pAttachedObject =  *attachmentIter;
+			if ( pAttachment && pAttachedObject->mDrawable.notNull() )
+			{
+				gPipeline.markRebuild(pAttachedObject->mDrawable);
+			}
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+// cleanupAttachedMesh()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
+{
+	//If a VO has a skin that we'll reset the joint positions to their default
+	if ( pVO && pVO->mDrawable )
+	{
+		LLVOVolume* pVObj = pVO->mDrawable->getVOVolume();
+		if ( pVObj )
+		{
+			const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID() );
+			if ( pSkinData )
+			{
+				const int jointCnt = pSkinData->mJointNames.size();
+				bool fullRig = ( jointCnt>=20 ) ? true : false;
+				if ( fullRig )
+				{
+					const int bindCnt = pSkinData->mAlternateBindMatrix.size();							
+					if ( bindCnt > 0 )
+					{
+						LLVOAvatar::resetJointPositionsToDefault();
+					}
+				}
+			}				
+		}
+	}	
+}
 //-----------------------------------------------------------------------------
 // detachObject()
 //-----------------------------------------------------------------------------
@@ -5780,14 +6044,23 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
 		 ++iter)
 	{
 		LLViewerJointAttachment* attachment = iter->second;
-
+		
 		if (attachment->isObjectAttached(viewer_object))
 		{
+			cleanupAttachedMesh( viewer_object );
 			attachment->removeObject(viewer_object);
 			lldebugs << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << llendl;
 			return TRUE;
 		}
 	}
+
+	std::vector<LLPointer<LLViewerObject> >::iterator iter = std::find(mPendingAttachment.begin(), mPendingAttachment.end(), viewer_object);
+	if (iter != mPendingAttachment.end())
+	{
+		mPendingAttachment.erase(iter);
+		return TRUE;
+	}
+	
 	return FALSE;
 }
 
@@ -7864,7 +8137,7 @@ BOOL LLVOAvatar::updateLOD()
 	BOOL res = updateJointLODs();
 
 	LLFace* facep = mDrawable->getFace(0);
-	if (facep->mVertexBuffer.isNull())
+	if (!facep->getVertexBuffer())
 	{
 		dirtyMesh(2);
 	}
@@ -7876,12 +8149,16 @@ BOOL LLVOAvatar::updateLOD()
 		mNeedsSkin = TRUE;
 		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
 	}
-	
 	updateVisibility();
 
 	return res;
 }
 
+void LLVOAvatar::updateLODRiggedAttachments( void )
+{
+	updateLOD();
+	rebuildRiggedAttachments();
+}
 U32 LLVOAvatar::getPartitionType() const
 { 
 	// Avatars merely exist as drawables in the bridge partition
@@ -7933,9 +8210,9 @@ void LLVOAvatar::cacheImpostorValues()
 	getImpostorValues(mImpostorExtents, mImpostorAngle, mImpostorDistance);
 }
 
-void LLVOAvatar::getImpostorValues(LLVector3* extents, LLVector3& angle, F32& distance) const
+void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& distance) const
 {
-	const LLVector3* ext = mDrawable->getSpatialExtents();
+	const LLVector4a* ext = mDrawable->getSpatialExtents();
 	extents[0] = ext[0];
 	extents[1] = ext[1];
 
@@ -7998,7 +8275,9 @@ void LLVOAvatar::idleUpdateRenderCost()
 				}
 			}
 		}
+
 	}
+
 	// Diagnostic output to identify all avatar-related textures.
 	// Does not affect rendering cost calculation.
 	// Could be wrapped in a debug option if output becomes problematic.
@@ -8038,6 +8317,7 @@ void LLVOAvatar::idleUpdateRenderCost()
 			}
 		}
 	}
+
 	cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
 
 	setDebugText(llformat("%d", cost));
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 3659fb055f743f05ac9f22a4e7919c61c01a0b36..295799fd247bf95a2c86f3bb999484ce2be9d799 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -123,10 +123,11 @@ class LLVOAvatar :
 	virtual BOOL   	 	 	idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 	virtual BOOL   	 	 	updateLOD();
 	BOOL  	 	 	 	 	updateJointLODs();
+	void					updateLODRiggedAttachments( void );
 	virtual BOOL   	 	 	isActive() const; // Whether this object needs to do an idleUpdate.
 	virtual void   	 	 	updateTextures();
 	virtual S32    	 	 	setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
-	virtual void   	 	 	onShift(const LLVector3& shift_vector);
+	virtual void   	 	 	onShift(const LLVector4a& shift_vector);
 	virtual U32    	 	 	getPartitionType() const;
 	virtual const  	 	 	LLVector3 getRenderPosition() const;
 	virtual void   	 	 	updateDrawable(BOOL force_damped);
@@ -134,8 +135,8 @@ class LLVOAvatar :
 	virtual BOOL   	 	 	updateGeometry(LLDrawable *drawable);
 	virtual void   	 	 	setPixelAreaAndAngle(LLAgent &agent);
 	virtual void   	 	 	updateRegion(LLViewerRegion *regionp);
-	virtual void   	 	 	updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax);
-	virtual void   	 	 	getSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+	virtual void   	 	 	updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
+	virtual void   	 	 	getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
 	virtual BOOL   	 	 	lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
 												 S32 face = -1,                    // which face to check, -1 = ALL_SIDES
 												 BOOL pick_transparent = FALSE,
@@ -167,7 +168,11 @@ class LLVOAvatar :
 
 	virtual LLJoint*		getJoint(const std::string &name);
 	virtual LLJoint*     	getRootJoint() { return &mRoot; }
-
+	
+	void					resetJointPositions( void );
+	void					resetJointPositionsToDefault( void );
+	void					resetSpecificJointPosition( const std::string& name );
+	
 	virtual const char*		getAnimationPrefix() { return "avatar"; }
 	virtual const LLUUID&   getID();
 	virtual LLVector3		getVolumePos(S32 joint_index, LLVector3& volume_offset);
@@ -196,6 +201,10 @@ class LLVOAvatar :
 public:
 	virtual bool 	isSelf() const { return false; } // True if this avatar is for this viewer's agent
 	bool isBuilt() const { return mIsBuilt; }
+
+private: //aligned members
+	LLVector4a	mImpostorExtents[2];
+
 private:
 	BOOL			mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients
 
@@ -285,6 +294,16 @@ class LLVOAvatar :
 public:
 	void				updateHeadOffset();
 	F32					getPelvisToFoot() const { return mPelvisToFoot; }
+	void				setPelvisOffset( bool hasOffset, const LLVector3& translation, F32 offset ) ;
+	bool				hasPelvisOffset( void ) { return mHasPelvisOffset; }
+	void				postPelvisSetRecalc( void );
+	void				setPelvisOffset( F32 pelvixFixupAmount );
+
+	bool				mHasPelvisOffset;
+	LLVector3			mPelvisOffset;
+	F32					mLastPelvisToFoot;
+	F32					mPelvisFixup;
+	F32					mLastPelvisFixup;
 
 	LLVector3			mHeadOffset; // current head position
 	LLViewerJoint		mRoot;
@@ -352,6 +371,8 @@ class LLVOAvatar :
 	U32 		renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
 	U32 		renderRigid();
 	U32 		renderSkinned(EAvatarRenderPass pass);
+	F32			getLastSkinTime() { return mLastSkinTime; }
+	U32			renderSkinnedAttachments();
 	U32 		renderTransparent(BOOL first_pass);
 	void 		renderCollisionVolumes();
 	static void	deleteCachedImages(bool clearAll=true);
@@ -363,6 +384,8 @@ class LLVOAvatar :
 	bool		shouldAlphaMask();
 
 	BOOL 		mNeedsSkin; // avatar has been animated and verts have not been updated
+	F32			mLastSkinTime; //value of gFrameTimeSeconds at last skin update
+
 	S32	 		mUpdatePeriod;
 	S32  		mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer.
 
@@ -402,7 +425,7 @@ class LLVOAvatar :
 	BOOL 	    needsImpostorUpdate() const;
 	const LLVector3& getImpostorOffset() const;
 	const LLVector2& getImpostorDim() const;
-	void 		getImpostorValues(LLVector3* extents, LLVector3& angle, F32& distance) const;
+	void 		getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& distance) const;
 	void 		cacheImpostorValues();
 	void 		setImpostorDim(const LLVector2& dim);
 	static void	resetImpostors();
@@ -413,7 +436,6 @@ class LLVOAvatar :
 	LLVector3	mImpostorOffset;
 	LLVector2	mImpostorDim;
 	BOOL		mNeedsAnimUpdate;
-	LLVector3	mImpostorExtents[2];
 	LLVector3	mImpostorAngle;
 	F32			mImpostorDistance;
 	F32			mImpostorPixelArea;
@@ -671,10 +693,12 @@ class LLVOAvatar :
 	void 				clampAttachmentPositions();
 	virtual const LLViewerJointAttachment* attachObject(LLViewerObject *viewer_object);
 	virtual BOOL 		detachObject(LLViewerObject *viewer_object);
+	void				cleanupAttachedMesh( LLViewerObject* pVO );
 	static LLVOAvatar*  findAvatarFromAttachment(LLViewerObject* obj);
 protected:
 	LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
 	void 				lazyAttach();
+	void				rebuildRiggedAttachments( void );
 
 	//--------------------------------------------------------------------
 	// Map of attachment points, by ID
@@ -786,6 +810,7 @@ class LLVOAvatar :
 	//--------------------------------------------------------------------
 public:
 	void 		resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
+	bool		distanceToGround( const LLVector3d &startPoint, LLVector3d &collisionPoint, F32 distToIntersectionAlongRay );
 	void 		resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
 	void 		resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
 	void 		slamPosition(); // Slam position to transmitted position (for teleport);
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 3b4066e3caa22a357d790e7e904960c38db2b17f..59883e0bb19be9867331c0a0c9194a5603d2dd10 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -56,6 +56,8 @@
 #include "llviewerstats.h"
 #include "llviewerregion.h"
 #include "llappearancemgr.h"
+#include "llmeshrepository.h"
+#include "llvovolume.h"
 
 #if LL_MSVC
 // disable boost::lexical_cast warning
@@ -623,6 +625,7 @@ BOOL LLVOAvatarSelf::updateCharacter(LLAgent &agent)
 		mScreenp->updateWorldMatrixChildren();
 		resetHUDAttachments();
 	}
+	
 	return LLVOAvatar::updateCharacter(agent);
 }
 
@@ -648,7 +651,11 @@ LLJoint *LLVOAvatarSelf::getJoint(const std::string &name)
 	}
 	return LLVOAvatar::getJoint(name);
 }
-
+//virtual
+void LLVOAvatarSelf::resetJointPositions( void )
+{
+	return LLVOAvatar::resetJointPositions();
+}
 // virtual
 BOOL LLVOAvatarSelf::setVisualParamWeight(LLVisualParam *which_param, F32 weight, BOOL upload_bake )
 {
@@ -1139,6 +1146,7 @@ const LLViewerJointAttachment *LLVOAvatarSelf::attachObject(LLViewerObject *view
 		LLAppearanceMgr::instance().registerAttachment(attachment_id);
 		// Clear any pending requests once the attachment arrives.
 		removeAttachmentRequest(attachment_id);
+		updateLODRiggedAttachments();		
 	}
 
 	return attachment;
@@ -1148,8 +1156,10 @@ const LLViewerJointAttachment *LLVOAvatarSelf::attachObject(LLViewerObject *view
 BOOL LLVOAvatarSelf::detachObject(LLViewerObject *viewer_object)
 {
 	const LLUUID attachment_id = viewer_object->getAttachmentItemID();
-	if (LLVOAvatar::detachObject(viewer_object))
+	if ( LLVOAvatar::detachObject(viewer_object) )
 	{
+		LLVOAvatar::cleanupAttachedMesh( viewer_object );
+		
 		// the simulator should automatically handle permission revocation
 		
 		stopMotionFromSource(attachment_id);
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index d13cf5ba38fceb8a9f93bdc3d911ddf2e6b47083..51f06dee5f5246e3572e1e434c6b8baecaeb14f5 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -83,7 +83,9 @@ class LLVOAvatarSelf :
 	/*virtual*/ void 		stopMotionFromSource(const LLUUID& source_id);
 	/*virtual*/ void 		requestStopMotion(LLMotion* motion);
 	/*virtual*/ LLJoint*	getJoint(const std::string &name);
-
+	
+				void		resetJointPositions( void );
+	
 	/*virtual*/ BOOL setVisualParamWeight(LLVisualParam *which_param, F32 weight, BOOL upload_bake = FALSE );
 	/*virtual*/ BOOL setVisualParamWeight(const char* param_name, F32 weight, BOOL upload_bake = FALSE );
 	/*virtual*/ BOOL setVisualParamWeight(S32 index, F32 weight, BOOL upload_bake = FALSE );
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index f57f7b67eab0399c574733ede631e1fa04ab64b7..32822e11816fe67772964bcefa5451c33d835e18 100644
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -340,7 +340,7 @@ void LLVOGrass::updateTextures()
 	{
 		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 		{
-			setDebugText(llformat("%4.0f", fsqrtf(mPixelArea)));
+			setDebugText(llformat("%4.0f", (F32) sqrt(mPixelArea)));
 		}
 		getTEImage(0)->addTextureStats(mPixelArea);
 	}
@@ -453,7 +453,7 @@ void LLVOGrass::plantBlades()
 	face->setTexture(getTEImage(0));
 	face->setState(LLFace::GLOBAL);
 	face->setSize(mNumBlades * 8, mNumBlades * 12);
-	face->mVertexBuffer = NULL;
+	face->setVertexBuffer(NULL);
 	face->setTEOffset(0);
 	face->mCenterLocal = mPosition + mRegionp->getOriginAgent();
 	
@@ -640,7 +640,7 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 
 	LLVector2 tc[4];
 	LLVector3 v[4];
-	// LLVector3 n[4]; // unused!
+	//LLVector3 n[4];
 
 	F32 closest_t = 1.f;
 
@@ -686,7 +686,6 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 		position.mV[2] += blade_height;
 		v[3]    = v1 = position + mRegionp->getOriginAgent();
 	
-
 		F32 a,b,t;
 
 		BOOL hit = FALSE;
@@ -694,23 +693,23 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 
 		U32 idx0 = 0,idx1 = 0,idx2 = 0;
 
-		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, &a, &b, &t, FALSE))
+		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE))
 		{
 			hit = TRUE;
 			idx0 = 0; idx1 = 1; idx2 = 2;
 		}
-		else if (LLTriangleRayIntersect(v[1], v[3], v[2], start, dir, &a, &b, &t, FALSE))
+		else if (LLTriangleRayIntersect(v[1], v[3], v[2], start, dir, a, b, t, FALSE))
 		{
 			hit = TRUE;
 			idx0 = 1; idx1 = 3; idx2 = 2;
 		}
-		else if (LLTriangleRayIntersect(v[2], v[1], v[0], start, dir, &a, &b, &t, FALSE))
+		else if (LLTriangleRayIntersect(v[2], v[1], v[0], start, dir, a, b, t, FALSE))
 		{
 			normal1 = -normal1;
 			hit = TRUE;
 			idx0 = 2; idx1 = 1; idx2 = 0;
 		}
-		else if (LLTriangleRayIntersect(v[2], v[3], v[1], start, dir, &a, &b, &t, FALSE))
+		else if (LLTriangleRayIntersect(v[2], v[3], v[1], start, dir, a, b, t, FALSE))
 		{
 			normal1 = -normal1;
 			hit = TRUE;
diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp
index f032ae8780e2340cf3976a640687206aefc813bb..ce256fdedf47371272a636bd6a931441f6f88b2b 100644
--- a/indra/newview/llvoground.cpp
+++ b/indra/newview/llvoground.cpp
@@ -97,13 +97,14 @@ BOOL LLVOGround::updateGeometry(LLDrawable *drawable)
 		drawable->addFace(poolp, NULL);
 	face = drawable->getFace(0); 
 		
-	if (face->mVertexBuffer.isNull())
+	if (!face->getVertexBuffer())
 	{
 		face->setSize(5, 12);
-		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolGround::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
-		face->mVertexBuffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
+		LLVertexBuffer* buff = new LLVertexBuffer(LLDrawPoolGround::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		buff->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
 		face->setGeomIndex(0);
 		face->setIndicesIndex(0);
+		face->setVertexBuffer(buff);
 	}
 	
 	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
@@ -161,7 +162,7 @@ BOOL LLVOGround::updateGeometry(LLDrawable *drawable)
 	*(texCoordsp++) = LLVector2(0.f, 1.f);
 	*(texCoordsp++) = LLVector2(0.5f, 0.5f);
 	
-	face->mVertexBuffer->setBuffer(0);
+	face->getVertexBuffer()->setBuffer(0);
 	LLPipeline::sCompiles++;
 	return TRUE;
 }
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 08581be38bec438e5d041b626f5803810d8891b8..6ee6822e2fcdc4eae6b7bc4f9d1a49d337c98e2c 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -6979,13 +6979,6 @@ void LLVivoxProtocolParser::reset()
 	alias.clear();
 	numberOfAliases = 0;
 	applicationString.clear();
-	id = 0;
-	nameString.clear();
-	descriptionString.clear();
-	expirationDate = LLDate();
-	hasExpired = false;
-	fontType = 0;
-	fontStatus = 0;
 }
 
 //virtual 
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index 40833ad25939dca0c18f436e0523745783a0cd41..6f354b78b1e1fa953e105dc98fd1e6330cd9e3d7 100644
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -73,12 +73,14 @@ F32 LLVOPartGroup::getBinRadius()
 	return mScale.mV[0]*2.f;
 }
 
-void LLVOPartGroup::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+void LLVOPartGroup::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {		
 	const LLVector3& pos_agent = getPositionAgent();
-	newMin = pos_agent - mScale;
-	newMax = pos_agent + mScale;
-	mDrawable->setPositionGroup(pos_agent);
+	newMin.load3( (pos_agent - mScale).mV);
+	newMax.load3( (pos_agent + mScale).mV);
+	LLVector4a pos;
+	pos.load3(pos_agent.mV);
+	mDrawable->setPositionGroup(pos);
 }
 
 BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
@@ -358,7 +360,7 @@ U32 LLVOPartGroup::getPartitionType() const
 }
 
 LLParticlePartition::LLParticlePartition()
-: LLSpatialPartition(LLDrawPoolAlpha::VERTEX_DATA_MASK, TRUE, GL_DYNAMIC_DRAW_ARB)
+: LLSpatialPartition(LLDrawPoolAlpha::VERTEX_DATA_MASK, TRUE, GL_STREAM_DRAW_ARB)
 {
 	mRenderPass = LLRenderPass::PASS_ALPHA;
 	mDrawableType = LLPipeline::RENDER_TYPE_PARTICLES;
@@ -461,7 +463,7 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 		LLAlphaObject* object = (LLAlphaObject*) facep->getViewerObject();
 		facep->setGeomIndex(vertex_count);
 		facep->setIndicesIndex(index_count);
-		facep->mVertexBuffer = buffer;
+		facep->setVertexBuffer(buffer);
 		facep->setPoolType(LLDrawPool::POOL_ALPHA);
 		object->getGeometry(facep->getTEOffset(), verticesp, normalsp, texcoordsp, colorsp, indicesp);
 		
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
index b136f2cbfaa281f00c4204a0e2169d15c1932678..4db893b4ef7f47580409446876fd30d386ebf5b6 100644
--- a/indra/newview/llvopartgroup.h
+++ b/indra/newview/llvopartgroup.h
@@ -51,7 +51,7 @@ class LLVOPartGroup : public LLAlphaObject
 	BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 
 	virtual F32 getBinRadius();
-	virtual void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+	virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
 	virtual U32 getPartitionType() const;
 	
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index 80f43e51d20464397c3ebd96ca77094bace7fc9a..6396bc042dba7fc9f4b01559b83adfb9fc25ae99 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -304,7 +304,7 @@ void LLSkyTex::createGLImage(S32 which)
 
 void LLSkyTex::bindTexture(BOOL curr)
 {
-	gGL.getTexUnit(0)->bind(mTexture[getWhich(curr)]);
+	gGL.getTexUnit(0)->bind(mTexture[getWhich(curr)], true);
 }
 
 /***************************************
@@ -1182,7 +1182,7 @@ BOOL LLVOSky::updateSky()
 		}
 	}
 
-	if (mDrawable.notNull() && mDrawable->getFace(0) && mDrawable->getFace(0)->mVertexBuffer.isNull())
+	if (mDrawable.notNull() && mDrawable->getFace(0) && !mDrawable->getFace(0)->getVertexBuffer())
 	{
 		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
 	}
@@ -1233,10 +1233,11 @@ void LLVOSky::createDummyVertexBuffer()
 		mFace[FACE_DUMMY] = mDrawable->addFace(poolp, NULL);
 	}
 
-	if(mFace[FACE_DUMMY]->mVertexBuffer.isNull())
+	if(!mFace[FACE_DUMMY]->getVertexBuffer())
 	{
-		mFace[FACE_DUMMY]->mVertexBuffer = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_DYNAMIC_DRAW_ARB);
-		mFace[FACE_DUMMY]->mVertexBuffer->allocateBuffer(1, 1, TRUE);
+		LLVertexBuffer* buff = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_DYNAMIC_DRAW_ARB);
+		buff->allocateBuffer(1, 1, TRUE);
+		mFace[FACE_DUMMY]->setVertexBuffer(buff);
 	}
 }
 
@@ -1255,13 +1256,13 @@ void LLVOSky::updateDummyVertexBuffer()
 
 	LLFastTimer t(FTM_RENDER_FAKE_VBO_UPDATE) ;
 
-	if(!mFace[FACE_DUMMY] || mFace[FACE_DUMMY]->mVertexBuffer.isNull())
+	if(!mFace[FACE_DUMMY] || !mFace[FACE_DUMMY]->getVertexBuffer())
 		createDummyVertexBuffer() ;
 
 	LLStrider<LLVector3> vertices ;
-	mFace[FACE_DUMMY]->mVertexBuffer->getVertexStrider(vertices,  0);
+	mFace[FACE_DUMMY]->getVertexBuffer()->getVertexStrider(vertices,  0);
 	*vertices = mCameraPosAgent ;
-	mFace[FACE_DUMMY]->mVertexBuffer->setBuffer(0) ;
+	mFace[FACE_DUMMY]->getVertexBuffer()->setBuffer(0) ;
 }
 //----------------------------------
 //end of fake vertex buffer updating
@@ -1304,14 +1305,15 @@ BOOL LLVOSky::updateGeometry(LLDrawable *drawable)
 	{
 		face = mFace[FACE_SIDE0 + side]; 
 
-		if (face->mVertexBuffer.isNull())
+		if (!face->getVertexBuffer())
 		{
 			face->setSize(4, 6);
 			face->setGeomIndex(0);
 			face->setIndicesIndex(0);
-			face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
-			face->mVertexBuffer->allocateBuffer(4, 6, TRUE);
-			
+			LLVertexBuffer* buff = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+			buff->allocateBuffer(4, 6, TRUE);
+			face->setVertexBuffer(buff);
+
 			index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
 			
 			S32 vtx = 0;
@@ -1344,7 +1346,7 @@ BOOL LLVOSky::updateGeometry(LLDrawable *drawable)
 			*indicesp++ = index_offset + 3;
 			*indicesp++ = index_offset + 2;
 
-			face->mVertexBuffer->setBuffer(0);
+			buff->setBuffer(0);
 		}
 	}
 
@@ -1471,13 +1473,14 @@ BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, cons
 
 	facep = mFace[f]; 
 
-	if (facep->mVertexBuffer.isNull())
+	if (!facep->getVertexBuffer())
 	{
-		facep->setSize(4, 6);		
-		facep->mVertexBuffer = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
-		facep->mVertexBuffer->allocateBuffer(facep->getGeomCount(), facep->getIndicesCount(), TRUE);
+		facep->setSize(4, 6);	
+		LLVertexBuffer* buff = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		buff->allocateBuffer(facep->getGeomCount(), facep->getIndicesCount(), TRUE);
 		facep->setGeomIndex(0);
 		facep->setIndicesIndex(0);
+		facep->setVertexBuffer(buff);
 	}
 
 	index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
@@ -1506,7 +1509,7 @@ BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, cons
 	*indicesp++ = index_offset + 2;
 	*indicesp++ = index_offset + 3;
 
-	facep->mVertexBuffer->setBuffer(0);
+	facep->getVertexBuffer()->setBuffer(0);
 
 	if (is_sun)
 	{
@@ -1875,13 +1878,14 @@ void LLVOSky::updateReflectionGeometry(LLDrawable *drawable, F32 H,
 
 	LLFace *face = mFace[FACE_REFLECTION]; 
 
-	if (face->mVertexBuffer.isNull() || quads*4 != face->getGeomCount())
+	if (!face->getVertexBuffer() || quads*4 != face->getGeomCount())
 	{
 		face->setSize(quads * 4, quads * 6);
-		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
-		face->mVertexBuffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
+		LLVertexBuffer* buff = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		buff->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
 		face->setIndicesIndex(0);
 		face->setGeomIndex(0);
+		face->setVertexBuffer(buff);
 	}
 	
 	LLStrider<LLVector3> verticesp;
@@ -2019,7 +2023,7 @@ void LLVOSky::updateReflectionGeometry(LLDrawable *drawable, F32 H,
 		}
 	}
 
-	face->mVertexBuffer->setBuffer(0);
+	face->getVertexBuffer()->setBuffer(0);
 }
 
 
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index 2eb43984887adacb618d174a8eb69b04eef146aa..dbcd4f50cadf6549509afcd1aee89ee4dd79601d 100644
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -54,27 +54,77 @@ class LLVertexBufferTerrain : public LLVertexBuffer
 		LLVertexBuffer(MAP_VERTEX | MAP_NORMAL | MAP_TEXCOORD0 | MAP_TEXCOORD1 | MAP_COLOR, GL_DYNAMIC_DRAW_ARB)
 	{
 		//texture coordinates 2 and 3 exist, but use the same data as texture coordinate 1
-		mOffsets[TYPE_TEXCOORD3] = mOffsets[TYPE_TEXCOORD2] = mOffsets[TYPE_TEXCOORD1];
-		mTypeMask |= MAP_TEXCOORD2 | MAP_TEXCOORD3;
 	};
 
-	/*// virtual
+	// virtual
 	void setupVertexBuffer(U32 data_mask) const
-	{		
-		if (LLDrawPoolTerrain::getDetailMode() == 0 || LLPipeline::sShadowRender)
+	{	
+		U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
+
+		//assume tex coords 2 and 3 are present
+		U32 type_mask = mTypeMask | MAP_TEXCOORD2 | MAP_TEXCOORD3;
+
+		if ((data_mask & type_mask) != data_mask)
 		{
-			LLVertexBuffer::setupVertexBuffer(data_mask);
+			llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
+		}
+
+		if (data_mask & MAP_NORMAL)
+		{
+			glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL]));
+		}
+		if (data_mask & MAP_TEXCOORD3)
+		{ //substitute tex coord 0 for tex coord 3
+			glClientActiveTextureARB(GL_TEXTURE3_ARB);
+			glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
+			glClientActiveTextureARB(GL_TEXTURE0_ARB);
+		}
+		if (data_mask & MAP_TEXCOORD2)
+		{ //substitute tex coord 0 for tex coord 2
+			glClientActiveTextureARB(GL_TEXTURE2_ARB);
+			glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
+			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
-		else if (data_mask & LLVertexBuffer::MAP_TEXCOORD1)
+		if (data_mask & MAP_TEXCOORD1)
 		{
-			LLVertexBuffer::setupVertexBuffer(data_mask);
+			glClientActiveTextureARB(GL_TEXTURE1_ARB);
+			glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1]));
+			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
-		else
+		if (data_mask & MAP_BINORMAL)
 		{
-			LLVertexBuffer::setupVertexBuffer(data_mask);
+			glClientActiveTextureARB(GL_TEXTURE2_ARB);
+			glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], (void*)(base + mOffsets[TYPE_BINORMAL]));
+			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
-		llglassertok();		
-	}*/
+		if (data_mask & MAP_TEXCOORD0)
+		{
+			glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
+		}
+		if (data_mask & MAP_COLOR)
+		{
+			glColorPointer(4, GL_UNSIGNED_BYTE, LLVertexBuffer::sTypeSize[TYPE_COLOR], (void*)(base + mOffsets[TYPE_COLOR]));
+		}
+		
+		if (data_mask & MAP_WEIGHT)
+		{
+			glVertexAttribPointerARB(1, 1, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT], (void*)(base + mOffsets[TYPE_WEIGHT]));
+		}
+
+		if (data_mask & MAP_WEIGHT4 && sWeight4Loc != -1)
+		{
+			glVertexAttribPointerARB(sWeight4Loc, 4, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], (void*)(base+mOffsets[TYPE_WEIGHT4]));
+		}
+
+		if (data_mask & MAP_CLOTHWEIGHT)
+		{
+			glVertexAttribPointerARB(4, 4, GL_FLOAT, TRUE,  LLVertexBuffer::sTypeSize[TYPE_CLOTHWEIGHT], (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
+		}
+		if (data_mask & MAP_VERTEX)
+		{
+			glVertexPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], (void*)(base + 0));
+		}
+	}
 };
 
 //============================================================================
@@ -840,7 +890,7 @@ void LLVOSurfacePatch::dirtyGeom()
 	if (mDrawable)
 	{
 		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
-		mDrawable->getFace(0)->mVertexBuffer = NULL;
+		mDrawable->getFace(0)->setVertexBuffer(NULL);
 		mDrawable->movePartition();
 	}
 }
@@ -939,7 +989,13 @@ BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector3& start, const LLVect
 
 	//step one meter at a time until intersection point found
 
-	const LLVector3* ext = mDrawable->getSpatialExtents();
+	//VECTORIZE THIS
+	const LLVector4a* exta = mDrawable->getSpatialExtents();
+
+	LLVector3 ext[2];
+	ext[0].set(exta[0].getF32ptr());
+	ext[1].set(exta[1].getF32ptr());
+
 	F32 rad = (delta*tdelta).magVecSquared();
 
 	F32 t = 0.f;
@@ -1001,13 +1057,16 @@ BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector3& start, const LLVect
 	return FALSE;
 }
 
-void LLVOSurfacePatch::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+void LLVOSurfacePatch::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
 {
 	LLVector3 posAgent = getPositionAgent();
 	LLVector3 scale = getScale();
-	newMin = posAgent-scale*0.5f; // Changing to 2.f makes the culling a -little- better, but still wrong
-	newMax = posAgent+scale*0.5f;
-	mDrawable->setPositionGroup((newMin+newMax)*0.5f);
+	newMin.load3( (posAgent-scale*0.5f).mV); // Changing to 2.f makes the culling a -little- better, but still wrong
+	newMax.load3( (posAgent+scale*0.5f).mV);
+	LLVector4a pos;
+	pos.setAdd(newMin,newMax);
+	pos.mul(0.5f);
+	mDrawable->setPositionGroup(pos);
 }
 
 U32 LLVOSurfacePatch::getPartitionType() const
@@ -1060,7 +1119,7 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
 
 		facep->setIndicesIndex(indices_index);
 		facep->setGeomIndex(index_offset);
-		facep->mVertexBuffer = buffer;
+		facep->setVertexBuffer(buffer);
 
 		LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
 		patchp->getGeometry(vertices, normals, colors, texcoords, texcoords2, indices);
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index bd80e1dbe6706489b189b965c53fc43ba121908c..8e75ff2e6e8be6b1c692b130ea171e6f97b64f15 100644
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -72,7 +72,7 @@ class LLVOSurfacePatch : public LLStaticViewerObject
 	/*virtual*/ void updateTextures();
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
 
-	/*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+	/*virtual*/ void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
 	/*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
 
 	void setPatch(LLSurfacePatch *patchp);
diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp
index b61dae53ba4ef57d2cd0fbf7477c047fad6407ef..a92172fe2306ee662a233490a7533d21a6e0628f 100644
--- a/indra/newview/llvotextbubble.cpp
+++ b/indra/newview/llvotextbubble.cpp
@@ -39,6 +39,7 @@
 #include "llviewertexturelist.h"
 #include "llvolume.h"
 #include "pipeline.h"
+#include "llvector4a.h"
 #include "llviewerregion.h"
 
 LLVOTextBubble::LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
@@ -211,7 +212,7 @@ void LLVOTextBubble::updateFaceSize(S32 idx)
 	else
 	{
 		const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
-		face->setSize(vol_face.mVertices.size(), vol_face.mIndices.size());
+		face->setSize(vol_face.mNumVertices, vol_face.mNumIndices);
 	}
 }
 
@@ -229,19 +230,37 @@ void LLVOTextBubble::getGeometry(S32 idx,
 
 	const LLVolumeFace& face = getVolume()->getVolumeFace(idx);
 	
-	LLVector3 pos = getPositionAgent();
+	LLVector4a pos;
+	pos.load3(getPositionAgent().mV);
+
+	LLVector4a scale;
+	scale.load3(getScale().mV);
+
 	LLColor4U color = LLColor4U(getTE(idx)->getColor());
 	U32 offset = mDrawable->getFace(idx)->getGeomIndex();
 	
-	for (U32 i = 0; i < face.mVertices.size(); i++)
+	LLVector4a* dst_pos = (LLVector4a*) verticesp.get();
+	LLVector4a* src_pos = (LLVector4a*) face.mPositions;
+	
+	LLVector4a* dst_norm = (LLVector4a*) normalsp.get();
+	LLVector4a* src_norm  = (LLVector4a*) face.mNormals;
+	
+	LLVector2* dst_tc = (LLVector2*) texcoordsp.get();
+	LLVector2* src_tc = (LLVector2*) face.mTexCoords;
+
+	LLVector4a::memcpyNonAliased16((F32*) dst_norm, (F32*) src_norm, face.mNumVertices*4*sizeof(F32));
+	LLVector4a::memcpyNonAliased16((F32*) dst_tc, (F32*) src_tc, face.mNumVertices*2*sizeof(F32));
+	
+	
+	for (U32 i = 0; i < face.mNumVertices; i++)
 	{
-		*verticesp++ = face.mVertices[i].mPosition.scaledVec(getScale()) + pos;
-		*normalsp++ = face.mVertices[i].mNormal;
-		*texcoordsp++ = face.mVertices[i].mTexCoord;
+		LLVector4a t;
+		t.setMul(src_pos[i], scale);
+		dst_pos[i].setAdd(t, pos);
 		*colorsp++ = color;
 	}
 	
-	for (U32 i = 0; i < face.mIndices.size(); i++)
+	for (U32 i = 0; i < face.mNumIndices; i++)
 	{
 		*indicesp++ = face.mIndices[i] + offset;
 	}
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
index 46025b46bea9887daa7d2adb9b96cf176e172f96..8946d4e0b60c2f0f0ba91aa1a827ff5b573f712e 100644
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -486,7 +486,7 @@ void LLVOTree::updateTextures()
 	{
 		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 		{
-			setDebugText(llformat("%4.0f", fsqrtf(mPixelArea)));
+			setDebugText(llformat("%4.0f", (F32) sqrt(mPixelArea)));
 		}
 		mTreeImagep->addTextureStats(mPixelArea);
 	}
@@ -526,11 +526,11 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 	if(mTrunkLOD >= sMAX_NUM_TREE_LOD_LEVELS) //do not display the tree.
 	{
 		mReferenceBuffer = NULL ;
-		mDrawable->getFace(0)->mVertexBuffer = NULL ;
+		mDrawable->getFace(0)->setVertexBuffer(NULL);
 		return TRUE ;
 	}
 
-	if (mReferenceBuffer.isNull() || mDrawable->getFace(0)->mVertexBuffer.isNull())
+	if (mReferenceBuffer.isNull() || !mDrawable->getFace(0)->getVertexBuffer())
 	{
 		const F32 SRR3 = 0.577350269f; // sqrt(1/3)
 		const F32 SRR2 = 0.707106781f; // sqrt(1/2)
@@ -864,7 +864,7 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 
 	if (gSavedSettings.getBOOL("RenderAnimateTrees"))
 	{
-		mDrawable->getFace(0)->mVertexBuffer = mReferenceBuffer;
+		mDrawable->getFace(0)->setVertexBuffer(mReferenceBuffer);
 	}
 	else
 	{
@@ -922,8 +922,9 @@ void LLVOTree::updateMesh()
 	calcNumVerts(vert_count, index_count, mTrunkLOD, stop_depth, mDepth, mTrunkDepth, mBranches);
 
 	LLFace* facep = mDrawable->getFace(0);
-	facep->mVertexBuffer = new LLVertexBuffer(LLDrawPoolTree::VERTEX_DATA_MASK, GL_STATIC_DRAW_ARB);
-	facep->mVertexBuffer->allocateBuffer(vert_count, index_count, TRUE);
+	LLVertexBuffer* buff = new LLVertexBuffer(LLDrawPoolTree::VERTEX_DATA_MASK, GL_STATIC_DRAW_ARB);
+	buff->allocateBuffer(vert_count, index_count, TRUE);
+	facep->setVertexBuffer(buff);
 	
 	LLStrider<LLVector3> vertices;
 	LLStrider<LLVector3> normals;
@@ -931,16 +932,15 @@ void LLVOTree::updateMesh()
 	LLStrider<U16> indices;
 	U16 idx_offset = 0;
 
-	facep->mVertexBuffer->getVertexStrider(vertices);
-	facep->mVertexBuffer->getNormalStrider(normals);
-	facep->mVertexBuffer->getTexCoord0Strider(tex_coords);
-	facep->mVertexBuffer->getIndexStrider(indices);
+	buff->getVertexStrider(vertices);
+	buff->getNormalStrider(normals);
+	buff->getTexCoord0Strider(tex_coords);
+	buff->getIndexStrider(indices);
 
 	genBranchPipeline(vertices, normals, tex_coords, indices, idx_offset, scale_mat, mTrunkLOD, stop_depth, mDepth, mTrunkDepth, 1.0, mTwist, droop, mBranches, alpha);
 	
 	mReferenceBuffer->setBuffer(0);
-	facep->mVertexBuffer->setBuffer(0);
-
+	buff->setBuffer(0);
 }
 
 void LLVOTree::appendMesh(LLStrider<LLVector3>& vertices, 
@@ -1261,7 +1261,7 @@ void LLVOTree::updateRadius()
 	mDrawable->setRadius(32.0f);
 }
 
-void LLVOTree::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+void LLVOTree::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {
 	F32 radius = getScale().length()*0.05f;
 	LLVector3 center = getRenderPosition();
@@ -1271,9 +1271,11 @@ void LLVOTree::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
 
 	center += LLVector3(0, 0, size.mV[2]) * getRotation();
 	
-	newMin.set(center-size);
-	newMax.set(center+size);
-	mDrawable->setPositionGroup(center);
+	newMin.load3((center-size).mV);
+	newMax.load3((center+size).mV);
+	LLVector4a pos;
+	pos.load3(center.mV);
+	mDrawable->setPositionGroup(pos);
 }
 
 BOOL LLVOTree::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
@@ -1286,8 +1288,13 @@ BOOL LLVOTree::lineSegmentIntersect(const LLVector3& start, const LLVector3& end
 		return FALSE;
 	}
 
-	const LLVector3* ext = mDrawable->getSpatialExtents();
+	const LLVector4a* exta = mDrawable->getSpatialExtents();
 
+	//VECTORIZE THIS
+	LLVector3 ext[2];
+	ext[0].set(exta[0].getF32ptr());
+	ext[1].set(exta[1].getF32ptr());
+	
 	LLVector3 center = (ext[1]+ext[0])*0.5f;
 	LLVector3 size = (ext[1]-ext[0]);
 
@@ -1324,7 +1331,7 @@ U32 LLVOTree::getPartitionType() const
 }
 
 LLTreePartition::LLTreePartition()
-: LLSpatialPartition(0, FALSE, 0)
+: LLSpatialPartition(0, FALSE, GL_DYNAMIC_DRAW_ARB)
 {
 	mDrawableType = LLPipeline::RENDER_TYPE_TREE;
 	mPartitionType = LLViewerRegion::PARTITION_TREE;
diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h
index fd0ebdf8e28a76a13ebba3a8ca63f75bd405635f..1e1deede2640e7cfabe9b576e3aec396de680e17 100644
--- a/indra/newview/llvotree.h
+++ b/indra/newview/llvotree.h
@@ -68,7 +68,7 @@ class LLVOTree : public LLViewerObject
 
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL		updateGeometry(LLDrawable *drawable);
-	/*virtual*/ void		updateSpatialExtents(LLVector3 &min, LLVector3 &max);
+	/*virtual*/ void		updateSpatialExtents(LLVector4a &min, LLVector4a &max);
 
 	virtual U32 getPartitionType() const;
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index ee54a938ba75f554dc193f4e6347960b43ce1331..e9a8c9b80aab16c5feccf7f1834cb8fd97b0d68b 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -30,12 +30,16 @@
 
 #include "llvovolume.h"
 
+#include <sstream>
+
 #include "llviewercontrol.h"
 #include "lldir.h"
 #include "llflexibleobject.h"
+#include "llfloatertools.h"
 #include "llmaterialtable.h"
 #include "llprimitive.h"
 #include "llvolume.h"
+#include "llvolumeoctree.h"
 #include "llvolumemgr.h"
 #include "llvolumemessage.h"
 #include "material_codes.h"
@@ -44,6 +48,7 @@
 #include "object_flags.h"
 #include "llagentconstants.h"
 #include "lldrawable.h"
+#include "lldrawpoolavatar.h"
 #include "lldrawpoolbump.h"
 #include "llface.h"
 #include "llspatialpartition.h"
@@ -51,19 +56,24 @@
 #include "llflexibleobject.h"
 #include "llsky.h"
 #include "lltexturefetch.h"
+#include "llvector4a.h"
 #include "llviewercamera.h"
 #include "llviewertexturelist.h"
+#include "llviewerobjectlist.h"
 #include "llviewerregion.h"
 #include "llviewertextureanim.h"
 #include "llworld.h"
 #include "llselectmgr.h"
 #include "pipeline.h"
 #include "llsdutil.h"
+#include "llmatrix4a.h"
 #include "llmediaentry.h"
 #include "llmediadataclient.h"
+#include "llmeshrepository.h"
 #include "llagent.h"
 #include "llviewermediafocus.h"
 #include "lldatapacker.h"
+#include "llvoavatar.h"
 #include "llvocache.h"
 
 const S32 MIN_QUIET_FRAMES_COALESCE = 30;
@@ -84,6 +94,7 @@ LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient =
 
 static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");
 static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
+static LLFastTimer::DeclareTimer FTM_VOLUME_TEXTURES("Volume Textures");
 
 // Implementation class of LLMediaDataClientObject.  See llmediadataclient.h
 class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
@@ -694,6 +705,7 @@ BOOL LLVOVolume::isVisible() const
 
 void LLVOVolume::updateTextureVirtualSize()
 {
+	LLFastTimer ftm(FTM_VOLUME_TEXTURES);
 	// Update the pixel area of all faces
 
 	if(!isVisible())
@@ -727,7 +739,7 @@ void LLVOVolume::updateTextureVirtualSize()
 		const LLTextureEntry *te = face->getTextureEntry();
 		LLViewerTexture *imagep = face->getTexture();
 		if (!imagep || !te ||			
-			face->mExtents[0] == face->mExtents[1])
+			face->mExtents[0].equals3(face->mExtents[1]))
 		{
 			continue;
 		}
@@ -786,10 +798,12 @@ void LLVOVolume::updateTextureVirtualSize()
 	if (isSculpted())
 	{
 		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
-		LLUUID id =  sculpt_params->getSculptTexture(); 
+		LLUUID id =  sculpt_params->getSculptTexture();
 		
 		updateSculptTexture();
 		
+		
+
 		if (mSculptTexture.notNull())
 		{
 			mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(),
@@ -830,6 +844,7 @@ void LLVOVolume::updateTextureVirtualSize()
 										  mSculptTexture->getHeight(), mSculptTexture->getWidth()));
 			}
 		}
+
 	}
 
 	if (getLightTextureID().notNull())
@@ -845,18 +860,18 @@ void LLVOVolume::updateTextureVirtualSize()
 																	*camera));
 		}	
 	}
-
+	
 	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 	{
-		setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
+		setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
 	}
  	else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
  	{
- 		setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
+ 		setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
  	}
 	else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
 	{
-		setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
+		setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
 	}
 
 	if (mPixelArea == 0)
@@ -867,7 +882,8 @@ void LLVOVolume::updateTextureVirtualSize()
 
 BOOL LLVOVolume::isActive() const
 {
-	return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive());
+	return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) || 
+		(mDrawable.notNull() && mDrawable->isActive());
 }
 
 BOOL LLVOVolume::setMaterial(const U8 material)
@@ -941,8 +957,35 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
 	return mDrawable;
 }
 
-BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
+BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bool unique_volume)
 {
+	LLVolumeParams volume_params = params_in;
+
+	S32 last_lod = mVolumep.notNull() ? LLVolumeLODGroup::getVolumeDetailFromScale(mVolumep->getDetail()) : -1;
+	S32 lod = mLOD;
+
+	BOOL is404 = FALSE;
+
+	if (isSculpted())
+	{
+		// if it's a mesh
+		if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+		{ //meshes might not have all LODs, get the force detail to best existing LOD
+
+			LLUUID mesh_id = volume_params.getSculptID();
+
+			//profile and path params don't matter for meshes
+			volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+			lod = gMeshRepo.getActualMeshLOD(volume_params, lod);
+			if (lod == -1)
+			{
+				is404 = TRUE;
+				lod = 0;
+			}
+		}
+	}
+
 	// Check if we need to change implementations
 	bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
 	if (is_flexible)
@@ -970,29 +1013,53 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
 		}
 	}
 	
-	if ((LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
+	if (is404)
+	{
+		setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI));
+	}
+
+	if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
 	{
 		mFaceMappingChanged = TRUE;
 		
 		if (mVolumeImpl)
 		{
-			mVolumeImpl->onSetVolume(volume_params, detail);
+			mVolumeImpl->onSetVolume(volume_params, mLOD);
 		}
 	
 		updateSculptTexture();
 
+
 		if (isSculpted())
 		{
 			updateSculptTexture();
-
-			if (mSculptTexture.notNull())
+			// if it's a mesh
+			if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
 			{
-				sculpt();
+				if (getVolume()->getNumVolumeFaces() == 0 || getVolume()->isTetrahedron())
+				{ 
+					//load request not yet issued, request pipeline load this mesh
+					LLUUID asset_id = volume_params.getSculptID();
+					S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
+					if (available_lod != lod)
+					{
+						LLPrimitive::setVolume(volume_params, available_lod);
+					}
+				}
+				
+			}
+			else // otherwise is sculptie
+			{
+				if (mSculptTexture.notNull())
+				{
+					sculpt();
+				}
 			}
 		}
 
 		return TRUE;
 	}
+
 	return FALSE;
 }
 
@@ -1000,7 +1067,7 @@ void LLVOVolume::updateSculptTexture()
 {
 	LLPointer<LLViewerFetchedTexture> old_sculpt = mSculptTexture;
 
-	if (isSculpted())
+	if (isSculpted() && !isMesh())
 	{
 		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
 		LLUUID id =  sculpt_params->getSculptTexture();
@@ -1028,6 +1095,14 @@ void LLVOVolume::updateSculptTexture()
 	
 }
 
+
+
+void LLVOVolume::notifyMeshLoaded()
+{ 
+	mSculptChanged = TRUE;
+	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+}
+
 // sculpt replaces generate() for sculpted surfaces
 void LLVOVolume::sculpt()
 {	
@@ -1131,10 +1206,26 @@ BOOL LLVOVolume::calcLOD()
 
 	S32 cur_detail = 0;
 	
-	F32 radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
-	F32 distance = mDrawable->mDistanceWRTCamera; //llmin(mDrawable->mDistanceWRTCamera, MAX_LOD_DISTANCE);
+	F32 radius;
+	F32 distance;
+
+	if (mDrawable->isState(LLDrawable::RIGGED))
+	{
+		LLVOAvatar* avatar = getAvatar(); 
+		distance = avatar->mDrawable->mDistanceWRTCamera;
+		radius = avatar->getBinRadius();
+	}
+	else
+	{
+		distance = mDrawable->mDistanceWRTCamera;
+		radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
+	}
+	
+	//hold onto unmodified distance for debugging
+	F32 debug_distance = distance;
+	
 	distance *= sDistanceFactor;
-			
+
 	F32 rampDist = LLVOVolume::sLODFactor * 2;
 	
 	if (distance < rampDist)
@@ -1151,6 +1242,12 @@ BOOL LLVOVolume::calcLOD()
 	cur_detail = computeLODDetail(llround(distance, 0.01f), 
 									llround(radius, 0.01f));
 
+
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO))
+	{
+		setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail));
+	}
+
 	if (cur_detail != mLOD)
 	{
 		mAppAngle = llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
@@ -1178,7 +1275,7 @@ BOOL LLVOVolume::updateLOD()
 		mLODChanged = TRUE;
 	}
 
-	lod_changed |= LLViewerObject::updateLOD();
+	lod_changed = lod_changed || LLViewerObject::updateLOD();
 	
 	return lod_changed;
 }
@@ -1214,6 +1311,11 @@ void LLVOVolume::updateFaceFlags()
 	for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
 	{
 		LLFace *face = mDrawable->getFace(i);
+		if (!face)
+		{
+			return;
+		}
+
 		BOOL fullbright = getTE(i)->getFullbright();
 		face->clearState(LLFace::FULLBRIGHT | LLFace::HUD_RENDER | LLFace::LIGHT);
 
@@ -1293,14 +1395,28 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 {
 	BOOL res = TRUE;
 
-	LLVector3 min,max;
+	LLVector4a min,max;
 
-	BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION);
+	min.clear();
+	max.clear();
 
-	for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+	BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED);
+
+//	bool rigged = false;
+	LLVolume* volume = mRiggedVolume;
+	if (!volume)
+	{
+		volume = getVolume();
+	}
+
+	for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++)
 	{
 		LLFace *face = mDrawable->getFace(i);
-		res &= face->genVolumeBBoxes(*getVolume(), i,
+		if (!face)
+		{
+			continue;
+		}
+		res &= face->genVolumeBBoxes(*volume, i,
 										mRelativeXform, mRelativeXformInvTrans,
 										(mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
 		
@@ -1313,17 +1429,8 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 			}
 			else
 			{
-				for (U32 i = 0; i < 3; i++)
-				{
-					if (face->mExtents[0].mV[i] < min.mV[i])
-					{
-						min.mV[i] = face->mExtents[0].mV[i];
-					}
-					if (face->mExtents[1].mV[i] > max.mV[i])
-					{
-						max.mV[i] = face->mExtents[1].mV[i];
-					}
-				}
+				min.setMin(min, face->mExtents[0]);
+				max.setMax(max, face->mExtents[1]);
 			}
 		}
 	}
@@ -1331,7 +1438,9 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 	if (rebuild)
 	{
 		mDrawable->setSpatialExtents(min,max);
-		mDrawable->setPositionGroup((min+max)*0.5f);	
+		min.add(max);
+		min.mul(0.5f);
+		mDrawable->setPositionGroup(min);	
 	}
 
 	updateRadius();
@@ -1358,7 +1467,21 @@ void LLVOVolume::updateRelativeXform()
 	
 	LLDrawable* drawable = mDrawable;
 	
-	if (drawable->isActive())
+	if (drawable->isState(LLDrawable::RIGGED) && mRiggedVolume.notNull())
+	{ //rigged volume (which is in agent space) is used for generating bounding boxes etc
+	  //inverse of render matrix should go to partition space
+		mRelativeXform = getRenderMatrix();
+
+		F32* dst = (F32*) mRelativeXformInvTrans.mMatrix;
+		F32* src = (F32*) mRelativeXform.mMatrix;
+		dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2];
+		dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6];
+		dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10];
+		
+		mRelativeXform.invert();
+		mRelativeXformInvTrans.transpose();
+	}
+	else if (drawable->isActive())
 	{				
 		// setup relative transforms
 		LLQuaternion delta_rot;
@@ -1440,11 +1563,22 @@ void LLVOVolume::updateRelativeXform()
 
 static LLFastTimer::DeclareTimer FTM_GEN_FLEX("Generate Flexies");
 static LLFastTimer::DeclareTimer FTM_UPDATE_PRIMITIVES("Update Primitives");
+static LLFastTimer::DeclareTimer FTM_UPDATE_RIGGED_VOLUME("Update Rigged");
 
 BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 {
 	LLFastTimer t(FTM_UPDATE_PRIMITIVES);
 	
+	if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
+	{
+		{
+			LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME);
+			updateRiggedVolume();
+		}
+		genBBoxes(FALSE);
+		mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
+	}
+
 	if (mVolumeImpl != NULL)
 	{
 		BOOL res;
@@ -1521,6 +1655,17 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 					regenFaces();
 				}
 				genBBoxes(FALSE);
+
+				if (mSculptChanged)
+				{ //changes in sculpt maps can thrash an object bounding box without 
+				  //triggering a spatial group bounding box update -- force spatial group
+				  //to update bounding boxes
+					LLSpatialGroup* group = mDrawable->getSpatialGroup();
+					if (group)
+					{
+						group->unbound();
+					}
+				}
 			}
 		}
 	}
@@ -1540,12 +1685,12 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 	{
 		LLPipeline::sCompiles++;
 	}
-	
+		
 	mVolumeChanged = FALSE;
 	mLODChanged = FALSE;
 	mSculptChanged = FALSE;
 	mFaceMappingChanged = FALSE;
-
+	
 	return LLViewerObject::updateGeometry(drawable);
 }
 
@@ -1554,19 +1699,14 @@ void LLVOVolume::updateFaceSize(S32 idx)
 	LLFace* facep = mDrawable->getFace(idx);
 	if (idx >= getVolume()->getNumVolumeFaces())
 	{
-		facep->setSize(0,0);
+		facep->setSize(0,0, true);
 	}
 	else
 	{
 		const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
-		if (LLPipeline::sUseTriStrips)
-		{
-			facep->setSize(vol_face.mVertices.size(), vol_face.mTriStrip.size());
-		}
-		else
-		{
-			facep->setSize(vol_face.mVertices.size(), vol_face.mIndices.size());
-		}
+		facep->setSize(vol_face.mNumVertices, vol_face.mNumIndices, 
+						true); // <--- volume faces should be padded for 16-byte alignment
+		
 	}
 }
 
@@ -1821,21 +1961,25 @@ bool LLVOVolume::hasMedia() const
 LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id)
 {
 	LLVolume* volume = getVolume();
-	LLVector3 result;
+	LLVector4a result;
+	result.clear();
+
+	LLVector3 ret;
 
 	if (volume && face_id < volume->getNumVolumeFaces())
 	{
 		const LLVolumeFace& face = volume->getVolumeFace(face_id);
-		for (S32 i = 0; i < (S32)face.mVertices.size(); ++i)
+		for (S32 i = 0; i < (S32)face.mNumVertices; ++i)
 		{
-			result += face.mVertices[i].mNormal;
+			result.add(face.mNormals[i]);
 		}
 
-		result = volumeDirectionToAgent(result);
-		result.normVec();
+		LLVector3 ret(result.getF32ptr());
+		ret = volumeDirectionToAgent(ret);
+		ret.normVec();
 	}
 	
-	return result;
+	return ret;
 }
 
 void LLVOVolume::requestMediaDataUpdate(bool isNew)
@@ -2632,6 +2776,23 @@ BOOL LLVOVolume::isSculpted() const
 	return FALSE;
 }
 
+BOOL LLVOVolume::isMesh() const
+{
+	if (isSculpted())
+	{
+		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+		U8 sculpt_type = sculpt_params->getSculptType();
+
+		if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+			// mesh is a mesh
+		{
+			return TRUE;	
+		}
+	}
+
+	return FALSE;
+}
+
 BOOL LLVOVolume::hasLightTexture() const
 {
 	if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
@@ -2648,6 +2809,11 @@ BOOL LLVOVolume::isVolumeGlobal() const
 	{
 		return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
 	}
+	else if (mRiggedVolume.notNull())
+	{
+		return TRUE;
+	}
+
 	return FALSE;
 }
 
@@ -2732,7 +2898,7 @@ void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_p
 			trans_mat.translate(getRegion()->getOriginAgent());
 		}
 
-		volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask());
+		volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask());
 
 		nodep->mSilhouetteExists = TRUE;
 	}
@@ -2918,6 +3084,62 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 
 }
 
+F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes)
+{
+	if (isMesh())
+	{	
+		LLSD& header = gMeshRepo.getMeshHeader(getVolume()->getParams().getSculptID());
+
+		F32 radius = getScale().length();
+		
+		return LLMeshRepository::getStreamingCost(header, radius, bytes, visible_bytes, mLOD);
+	}
+		
+	return 0.f;
+}
+
+U32 LLVOVolume::getTriangleCount()
+{
+	U32 count = 0;
+	LLVolume* volume = getVolume();
+	if (volume)
+	{
+		count = volume->getNumTriangles();
+	}
+
+	return count;
+}
+
+U32 LLVOVolume::getHighLODTriangleCount()
+{
+	U32 ret = 0;
+
+	LLVolume* volume = getVolume();
+
+	if (!isSculpted())
+	{
+		LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
+		ret = ref->getNumTriangles();
+		LLPrimitive::getVolumeManager()->unrefVolume(ref);
+	}
+	else if (isMesh())
+	{
+		LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
+		if (ref->isTetrahedron() || ref->getNumVolumeFaces() == 0)
+		{
+			gMeshRepo.loadMesh(this, volume->getParams(), LLModel::LOD_HIGH);
+		}
+		ret = ref->getNumTriangles();
+		LLPrimitive::getVolumeManager()->unrefVolume(ref);
+	}
+	else
+	{ //default sculpts have a constant number of triangles
+		ret = 31*2*31;  //31 rows of 31 columns of quads for a 32x32 vertex patch
+	}
+
+	return ret;
+}
+
 //static
 void LLVOVolume::preUpdateGeom()
 {
@@ -2955,7 +3177,7 @@ void LLVOVolume::setSelected(BOOL sel)
 	}
 }
 
-void LLVOVolume::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {		
 }
 
@@ -2963,7 +3185,9 @@ F32 LLVOVolume::getBinRadius()
 {
 	F32 radius;
 	
-	const LLVector3* ext = mDrawable->getSpatialExtents();
+	F32 scale = 1.f;
+
+	const LLVector4a* ext = mDrawable->getSpatialExtents();
 	
 	BOOL shrink_wrap = mDrawable->isAnimating();
 	BOOL alpha_wrap = FALSE;
@@ -2995,7 +3219,10 @@ F32 LLVOVolume::getBinRadius()
 	}
 	else if (shrink_wrap)
 	{
-		radius = (ext[1]-ext[0]).length()*0.5f;
+		LLVector4a rad;
+		rad.setSub(ext[1], ext[0]);
+		
+		radius = rad.getLength3().getF32()*0.5f;
 	}
 	else if (mDrawable->isStatic())
 	{
@@ -3019,7 +3246,7 @@ F32 LLVOVolume::getBinRadius()
 		radius = 8.f;
 	}
 
-	return llclamp(radius, 0.5f, 256.f);
+	return llclamp(radius*scale, 0.5f, 256.f);
 }
 
 const LLVector3 LLVOVolume::getPivotPositionAgent() const
@@ -3031,7 +3258,7 @@ const LLVector3 LLVOVolume::getPivotPositionAgent() const
 	return LLViewerObject::getPivotPositionAgent();
 }
 
-void LLVOVolume::onShift(const LLVector3 &shift_vector)
+void LLVOVolume::onShift(const LLVector4a &shift_vector)
 {
 	if (mVolumeImpl)
 	{
@@ -3108,12 +3335,37 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 	BOOL ret = FALSE;
 
 	LLVolume* volume = getVolume();
+
+	bool transform = true;
+
+	if (mDrawable->isState(LLDrawable::RIGGED))
+	{
+		if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf())
+		{
+			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_RIGGED, TRUE);
+			volume = mRiggedVolume;
+			transform = false;
+		}
+		else
+		{ //cannot pick rigged attachments on other avatars or when not in build mode
+			return FALSE;
+		}
+	}
+	
 	if (volume)
 	{	
 		LLVector3 v_start, v_end, v_dir;
 	
-		v_start = agentPositionToVolume(start);
-		v_end = agentPositionToVolume(end);
+		if (transform)
+		{
+			v_start = agentPositionToVolume(start);
+			v_end = agentPositionToVolume(end);
+		}
+		else
+		{
+			v_start = start;
+			v_end = end;
+		}
 		
 		LLVector3 p;
 		LLVector3 n;
@@ -3154,8 +3406,15 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 			end_face = face+1;
 		}
 
+		bool special_cursor = specialHoverCursor();
 		for (S32 i = start_face; i < end_face; ++i)
 		{
+			if (!special_cursor && !pick_transparent && getTE(i)->getColor().mV[3] == 0.f)
+			{ //don't attempt to pick completely transparent faces unless
+				//pick_transparent is true
+				continue;
+			}
+
 			face_hit = volume->lineSegmentIntersect(v_start, v_end, i,
 													&p, &tc, &n, &bn);
 			
@@ -3173,18 +3432,40 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 					
 					if (intersection != NULL)
 					{
-						*intersection = volumePositionToAgent(p);  // must map back to agent space
+						if (transform)
+						{
+							*intersection = volumePositionToAgent(p);  // must map back to agent space
+						}
+						else
+						{
+							*intersection = p;
+						}
 					}
 
 					if (normal != NULL)
 					{
-						*normal = volumeDirectionToAgent(n);
+						if (transform)
+						{
+							*normal = volumeDirectionToAgent(n);
+						}
+						else
+						{
+							*normal = n;
+						}
+
 						(*normal).normVec();
 					}
 
 					if (bi_normal != NULL)
 					{
-						*bi_normal = volumeDirectionToAgent(bn);
+						if (transform)
+						{
+							*bi_normal = volumeDirectionToAgent(bn);
+						}
+						else
+						{
+							*bi_normal = bn;
+						}
 						(*bi_normal).normVec();
 					}
 
@@ -3202,6 +3483,201 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 	return ret;
 }
 
+bool LLVOVolume::treatAsRigged()
+{
+	return LLFloater::isVisible(gFloaterTools) && 
+			isAttachment() && 
+			getAvatar() &&
+			getAvatar()->isSelf() &&
+			mDrawable.notNull() &&
+			mDrawable->isState(LLDrawable::RIGGED);
+}
+
+LLRiggedVolume* LLVOVolume::getRiggedVolume()
+{
+	return mRiggedVolume;
+}
+
+void LLVOVolume::clearRiggedVolume()
+{
+	if (mRiggedVolume.notNull())
+	{
+		mRiggedVolume = NULL;
+		updateRelativeXform();
+	}
+}
+
+void LLVOVolume::updateRiggedVolume()
+{
+	//Update mRiggedVolume to match current animation frame of avatar. 
+	//Also update position/size in octree.  
+
+	if (!treatAsRigged())
+	{
+		clearRiggedVolume();
+		
+		return;
+	}
+
+	LLVolume* volume = getVolume();
+
+	const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID());
+
+	if (!skin)
+	{
+		clearRiggedVolume();
+		return;
+	}
+
+	LLVOAvatar* avatar = getAvatar();
+
+	if (!avatar)
+	{
+		clearRiggedVolume();
+		return;
+	}
+
+	if (!mRiggedVolume)
+	{
+		LLVolumeParams p;
+		mRiggedVolume = new LLRiggedVolume(p);
+		updateRelativeXform();
+	}
+
+	mRiggedVolume->update(skin, avatar, volume);
+
+}
+
+static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin");
+static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree");
+
+void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume)
+{
+	bool copy = false;
+	if (volume->getNumVolumeFaces() != getNumVolumeFaces())
+	{ 
+		copy = true;
+	}
+
+	for (S32 i = 0; i < volume->getNumVolumeFaces() && !copy; ++i)
+	{
+		const LLVolumeFace& src_face = volume->getVolumeFace(i);
+		const LLVolumeFace& dst_face = getVolumeFace(i);
+
+		if (src_face.mNumIndices != dst_face.mNumIndices ||
+			src_face.mNumVertices != dst_face.mNumVertices)
+		{
+			copy = true;
+		}
+	}
+
+	if (copy)
+	{
+		copyVolumeFaces(volume);	
+	}
+
+	//build matrix palette
+	LLMatrix4a mp[64];
+	LLMatrix4* mat = (LLMatrix4*) mp;
+	
+	for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+	{
+		LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
+		if (joint)
+		{
+			mat[j] = skin->mInvBindMatrix[j];
+			mat[j] *= joint->getWorldMatrix();
+		}
+	}
+
+	for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+	{
+		const LLVolumeFace& vol_face = volume->getVolumeFace(i);
+		
+		LLVolumeFace& dst_face = mVolumeFaces[i];
+		
+		LLVector4a* weight = vol_face.mWeights;
+
+		LLMatrix4a bind_shape_matrix;
+		bind_shape_matrix.loadu(skin->mBindShapeMatrix);
+
+		LLVector4a* pos = dst_face.mPositions;
+
+		{
+			LLFastTimer t(FTM_SKIN_RIGGED);
+
+			for (U32 j = 0; j < dst_face.mNumVertices; ++j)
+			{
+				LLMatrix4a final_mat;
+				final_mat.clear();
+
+				S32 idx[4];
+
+				LLVector4 wght;
+
+				F32 scale = 0.f;
+				for (U32 k = 0; k < 4; k++)
+				{
+					F32 w = weight[j][k];
+
+					idx[k] = (S32) floorf(w);
+					wght[k] = w - floorf(w);
+					scale += wght[k];
+				}
+
+				wght *= 1.f/scale;
+
+				for (U32 k = 0; k < 4; k++)
+				{
+					F32 w = wght[k];
+
+					LLMatrix4a src;
+					src.setMul(mp[idx[k]], w);
+
+					final_mat.add(src);
+				}
+
+				
+				LLVector4a& v = vol_face.mPositions[j];
+				LLVector4a t;
+				LLVector4a dst;
+				bind_shape_matrix.affineTransform(v, t);
+				final_mat.affineTransform(t, dst);
+				pos[j] = dst;
+			}
+
+			//update bounding box
+			LLVector4a& min = dst_face.mExtents[0];
+			LLVector4a& max = dst_face.mExtents[1];
+
+			min = pos[0];
+			max = pos[1];
+
+			for (U32 j = 1; j < dst_face.mNumVertices; ++j)
+			{
+				min.setMin(min, pos[j]);
+				max.setMax(max, pos[j]);
+			}
+
+			dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]);
+			dst_face.mCenter->mul(0.5f);
+
+		}
+
+		{
+			LLFastTimer t(FTM_RIGGED_OCTREE);
+			delete dst_face.mOctree;
+			dst_face.mOctree = NULL;
+
+			LLVector4a size;
+			size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]);
+			size.splat(size.getLength3().getF32()*0.5f);
+			
+			dst_face.createOctree(1.f);
+		}
+	}
+}
+
 U32 LLVOVolume::getPartitionType() const
 {
 	if (isHUDAttachment())
@@ -3254,7 +3730,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		(type == LLRenderPass::PASS_INVISIBLE) ||
 		(type == LLRenderPass::PASS_ALPHA && facep->isState(LLFace::FULLBRIGHT));
 	
-	if (!fullbright && type != LLRenderPass::PASS_GLOW && !facep->mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL))
+	if (!fullbright && type != LLRenderPass::PASS_GLOW && !facep->getVertexBuffer()->hasDataType(LLVertexBuffer::TYPE_NORMAL))
 	{
 		llwarns << "Non fullbright face has no normals!" << llendl;
 		return;
@@ -3276,22 +3752,20 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 	else
 	{
 		model_mat = &(drawable->getRegion()->mRenderMatrix);
+		if (model_mat->isIdentity())
+		{
+			model_mat = NULL;
+		}
 	}
 
-
 	U8 bump = (type == LLRenderPass::PASS_BUMP || type == LLRenderPass::PASS_POST_BUMP) ? facep->getTextureEntry()->getBumpmap() : 0;
 	
 	LLViewerTexture* tex = facep->getTexture();
 
 	U8 glow = (U8) (facep->getTextureEntry()->getGlow() * 255);
 
-	if (facep->mVertexBuffer.isNull())
-	{
-		llerrs << "WTF?" << llendl;
-	}
-
 	if (idx >= 0 && 
-		draw_vec[idx]->mVertexBuffer == facep->mVertexBuffer &&
+		draw_vec[idx]->mVertexBuffer == facep->getVertexBuffer() &&
 		draw_vec[idx]->mEnd == facep->getGeomIndex()-1 &&
 		(LLPipeline::sTextureBindTest || draw_vec[idx]->mTexture == tex) &&
 #if LL_DARWIN
@@ -3307,7 +3781,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		draw_vec[idx]->mCount += facep->getIndicesCount();
 		draw_vec[idx]->mEnd += facep->getGeomCount();
 		draw_vec[idx]->mVSize = llmax(draw_vec[idx]->mVSize, facep->getVirtualSize());
-		validate_draw_info(*draw_vec[idx]);
+		draw_vec[idx]->validate();
 		update_min_max(draw_vec[idx]->mExtents[0], draw_vec[idx]->mExtents[1], facep->mExtents[0]);
 		update_min_max(draw_vec[idx]->mExtents[0], draw_vec[idx]->mExtents[1], facep->mExtents[1]);
 	}
@@ -3318,7 +3792,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		U32 offset = facep->getIndicesStart();
 		U32 count = facep->getIndicesCount();
 		LLPointer<LLDrawInfo> draw_info = new LLDrawInfo(start,end,count,offset, tex, 
-			facep->mVertexBuffer, fullbright, bump); 
+			facep->getVertexBuffer(), fullbright, bump); 
 		draw_info->mGroup = group;
 		draw_info->mVSize = facep->getVirtualSize();
 		draw_vec.push_back(draw_info);
@@ -3331,12 +3805,13 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		}
 		draw_info->mExtents[0] = facep->mExtents[0];
 		draw_info->mExtents[1] = facep->mExtents[1];
-		validate_draw_info(*draw_info);
 
 		if (LLPipeline::sUseTriStrips)
 		{
 			draw_info->mDrawMode = LLRender::TRIANGLE_STRIP;
 		}
+
+		draw_info->validate();
 	}
 }
 
@@ -3348,6 +3823,32 @@ void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group)
 static LLFastTimer::DeclareTimer FTM_REBUILD_VOLUME_VB("Volume");
 static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
 
+static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj)
+{
+	LLVOAvatar* avatar = vobj->getAvatar();
+					
+	if (avatar)
+	{
+		LLDrawable* drawable = avatar->mDrawable;
+		if (drawable && drawable->getNumFaces() > 0)
+		{
+			LLFace* face = drawable->getFace(0);
+			if (face)
+			{
+				LLDrawPool* drawpool = face->getPool();
+				if (drawpool)
+				{
+					if (drawpool->getType() == LLDrawPool::POOL_AVATAR)
+					{
+						return (LLDrawPoolAvatar*) drawpool;
+					}
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
 
 void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 {
@@ -3386,8 +3887,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 	std::vector<LLFace*> alpha_faces;
 	U32 useage = group->mSpatialPartition->mBufferUsage;
 
-	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcStride(group->mSpatialPartition->mVertexDataMask);
-	U32 max_total = (gSavedSettings.getS32("RenderMaxNodeSize")*1024)/LLVertexBuffer::calcStride(group->mSpatialPartition->mVertexDataMask);
+	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
+	U32 max_total = (gSavedSettings.getS32("RenderMaxNodeSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
 	max_vertices = llmin(max_vertices, (U32) 65535);
 
 	U32 cur_total = 0;
@@ -3408,29 +3909,210 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 		}
 
 		LLVOVolume* vobj = drawablep->getVOVolume();
+
+		if (vobj->getVolume() && vobj->getVolume()->isTetrahedron())
+		{
+			continue;
+		}
+
 		llassert_always(vobj);
 		vobj->updateTextureVirtualSize();
 		vobj->preRebuild();
 
 		drawablep->clearState(LLDrawable::HAS_ALPHA);
 
+		bool rigged = vobj->isAttachment() && 
+					vobj->isMesh() && 
+					gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID());
+
+		bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
+
+		bool is_rigged = false;
+
 		//for each face
 		for (S32 i = 0; i < drawablep->getNumFaces(); i++)
 		{
+			LLFace* facep = drawablep->getFace(i);
+
+			//ALWAYS null out vertex buffer on rebuild -- if the face lands in a render
+			// batch, it will recover its vertex buffer reference from the spatial group
+			facep->setVertexBuffer(NULL);
+			
 			//sum up face verts and indices
 			drawablep->updateFaceSize(i);
-			LLFace* facep = drawablep->getFace(i);
+			
+			
+
+			if (rigged) 
+			{
+				if (!facep->isState(LLFace::RIGGED))
+				{ //completely reset vertex buffer
+					facep->clearVertexBuffer();
+				}
+		
+				facep->setState(LLFace::RIGGED);
+				is_rigged = true;
+				
+				//get drawpool of avatar with rigged face
+				LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);
+				
+				//Determine if we've received skininfo that contains an
+				//alternate bind matrix - if it does then apply the translational component
+				//to the joints of the avatar.
+				LLVOAvatar* pAvatarVO = vobj->getAvatar();
+				bool pelvisGotSet = false;
+
+				if ( pAvatarVO )
+				{
+					LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
+					const LLMeshSkinInfo*  pSkinData = gMeshRepo.getSkinInfo( currentId );
+					
+					if ( pSkinData )
+					{
+						const int bindCnt = pSkinData->mAlternateBindMatrix.size();								
+						if ( bindCnt > 0 )
+						{					
+							const int jointCnt = pSkinData->mJointNames.size();
+							const F32 pelvisZOffset = pSkinData->mPelvisOffset;
+							bool fullRig = (jointCnt>=20) ? true : false;
+							if ( fullRig )
+							{
+								for ( int i=0; i<jointCnt; ++i )
+								{
+									std::string lookingForJoint = pSkinData->mJointNames[i].c_str();
+									//llinfos<<"joint name "<<lookingForJoint.c_str()<<llendl;
+									LLJoint* pJoint = pAvatarVO->getJoint( lookingForJoint );
+									if ( pJoint && pJoint->getId() != currentId )
+									{   									
+										pJoint->setId( currentId );
+										const LLVector3& jointPos = pSkinData->mAlternateBindMatrix[i].getTranslation();									
+										//Set the joint position
+										pJoint->storeCurrentXform( jointPos );																																
+										//If joint is a pelvis then handle old/new pelvis to foot values
+										if ( lookingForJoint == "mPelvis" )
+										{	
+											pJoint->storeCurrentXform( jointPos );																																
+											if ( !pAvatarVO->hasPelvisOffset() )
+											{										
+												pAvatarVO->setPelvisOffset( true, jointPos, pelvisZOffset );
+												//Trigger to rebuild viewer AV
+												pelvisGotSet = true;											
+											}										
+										}										
+									}
+								}
+							}							
+						}
+					}
+				}
+				//If we've set the pelvis to a new position we need to also rebuild some information that the
+				//viewer does at launch (e.g. body size etc.)
+				if ( pelvisGotSet )
+				{
+					pAvatarVO->postPelvisSetRecalc();
+				}
+
+				if (pool)
+				{
+					const LLTextureEntry* te = facep->getTextureEntry();
+
+					//remove face from old pool if it exists
+					LLDrawPool* old_pool = facep->getPool();
+					if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR)
+					{
+						((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep);
+					}
+
+					//add face to new pool
+					LLViewerTexture* tex = facep->getTexture();
+					U32 type = gPipeline.getPoolTypeFromTE(te, tex);
+
+					if (type == LLDrawPool::POOL_ALPHA)
+					{
+						if (te->getFullbright())
+						{
+							pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA);
+						}
+						else
+						{
+							pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_ALPHA);
+						}
+					}
+					else if (te->getShiny())
+					{
+						if (te->getFullbright())
+						{
+							pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY);
+						}
+						else
+						{
+							if (LLPipeline::sRenderDeferred)
+							{
+								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
+							}
+							else
+							{
+								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY);
+							}
+						}
+					}
+					else
+					{
+						if (te->getFullbright())
+						{
+							pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT);
+						}
+						else
+						{
+							pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
+						}
+					}
+
+					if (te->getGlow())
+					{
+						pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW);
+					}
+
+					if (LLPipeline::sRenderDeferred)
+					{
+						if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright())
+						{
+							if (te->getBumpmap())
+							{
+								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP);
+							}
+							else
+							{
+								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE);
+							}
+						}
+					}
+				}
+
+				continue;
+			}
+			else
+			{
+				if (facep->isState(LLFace::RIGGED))
+				{ //face is not rigged but used to be, remove from rigged face pool
+					LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
+					if (pool)
+					{
+						pool->removeRiggedFace(facep);
+					}
+					facep->clearState(LLFace::RIGGED);
+				}
+			}
 
 			if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0)
 			{
-				facep->mVertexBuffer = NULL;
-				facep->mLastVertexBuffer = NULL;
+				facep->clearVertexBuffer();
 				continue;
 			}
 
 			cur_total += facep->getGeomCount();
 
-			if (facep->hasGeometry() && facep->mPixelArea > FORCE_CULL_AREA)
+			if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
 			{
 				const LLTextureEntry* te = facep->getTextureEntry();
 				LLViewerTexture* tex = facep->getTexture();
@@ -3443,7 +4125,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 					}
 				}
 
-				BOOL force_simple = (facep->mPixelArea < FORCE_SIMPLE_RENDER_AREA);
+				BOOL force_simple = (facep->getPixelArea() < FORCE_SIMPLE_RENDER_AREA);
 				U32 type = gPipeline.getPoolTypeFromTE(te, tex);
 				if (type != LLDrawPool::POOL_ALPHA && force_simple)
 				{
@@ -3515,7 +4197,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 							bump_faces.push_back(facep);
 						}
 						else if ((te->getShiny() && LLPipeline::sRenderBump) ||
-							!te->getFullbright())
+							!(te->getFullbright() || bake_sunlight))
 						{ //needs normal
 							simple_faces.push_back(facep);
 						}
@@ -3529,10 +4211,18 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 			}
 			else
 			{	//face has no renderable geometry
-				facep->mVertexBuffer = NULL;
-				facep->mLastVertexBuffer = NULL;
+				facep->clearVertexBuffer();
 			}		
 		}
+
+		if (is_rigged)
+		{
+			drawablep->setState(LLDrawable::RIGGED);
+		}
+		else
+		{
+			drawablep->clearState(LLDrawable::RIGGED);
+		}
 	}
 
 	group->mBufferUsage = useage;
@@ -3547,7 +4237,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 	{
 		bump_mask |= LLVertexBuffer::MAP_BINORMAL;
 	}
-
+	
 	genDrawInfo(group, simple_mask, simple_faces);
 	genDrawInfo(group, bump_mask, bump_faces);
 	genDrawInfo(group, fullbright_mask, fullbright_faces);
@@ -3593,24 +4283,25 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 			LLFastTimer t(FTM_VOLUME_GEOM_PARTIAL);
 			LLDrawable* drawablep = *drawable_iter;
 
-			if (drawablep->isDead() || drawablep->isState(LLDrawable::FORCE_INVISIBLE) )
-			{
-				continue;
-			}
-
-			if (drawablep->isState(LLDrawable::REBUILD_ALL))
+			if (!drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) )
 			{
 				LLVOVolume* vobj = drawablep->getVOVolume();
 				vobj->preRebuild();
+
 				LLVolume* volume = vobj->getVolume();
 				for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
 				{
 					LLFace* face = drawablep->getFace(i);
-					if (face && face->mVertexBuffer.notNull())
+					if (face && face->getVertexBuffer())
 					{
 						face->getGeometryVolume(*volume, face->getTEOffset(), 
 							vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex());
 					}
+
+					if (!face)
+					{
+						llerrs << "WTF?" << llendl;
+					}
 				}
 
 				drawablep->clearState(LLDrawable::REBUILD_ALL);
@@ -3653,9 +4344,10 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 				for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
 				{
 					LLFace* face = drawablep->getFace(i);
-					if (face && face->mVertexBuffer.notNull() && face->mVertexBuffer->isLocked())
+					LLVertexBuffer* buff = face->getVertexBuffer();
+					if (face && buff && buff->isLocked())
 					{
-						face->mVertexBuffer->setBuffer(0) ;
+						buff->setBuffer(0) ;
 					}
 				}
 			} 
@@ -3673,7 +4365,7 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector<LLFace*>& faces, BOOL distance_sort)
 {
 	//calculate maximum number of vertices to store in a single buffer
-	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcStride(group->mSpatialPartition->mVertexDataMask);
+	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
 	max_vertices = llmin(max_vertices, (U32) 65535);
 
 	if (!distance_sort)
@@ -3720,10 +4412,12 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 			buffer_index = 0;
 		}
 
+		bool bake_sunlight = LLPipeline::sBakeSunlight && facep->getDrawable()->isStatic(); 
+
 		U32 index_count = facep->getIndicesCount();
 		U32 geom_count = facep->getGeomCount();
 
-		//sum up vertices needed for this texture
+		//sum up vertices needed for this render batch
 		std::vector<LLFace*>::iterator i = face_iter;
 		++i;
 		
@@ -3733,7 +4427,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 			facep = *i;
 			
 			if (geom_count + facep->getGeomCount() > max_vertices)
-			{ //cut vertex buffers on geom count too big
+			{ //cut batches on geom count too big
 				break;
 			}
 
@@ -3761,10 +4455,11 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 			buffer->allocateBuffer(geom_count, index_count, TRUE);
 		}
 		else 
-		{
-			if (LLVertexBuffer::sEnableVBOs && buffer->getUsage() != group->mBufferUsage)
+		{ //resize pre-existing buffer
+			if (LLVertexBuffer::sEnableVBOs && buffer->getUsage() != group->mBufferUsage ||
+				buffer->getTypeMask() != mask)
 			{
-				buffer = createVertexBuffer(group->mSpatialPartition->mVertexDataMask, 
+				buffer = createVertexBuffer(mask, 
 											group->mBufferUsage);
 				buffer->allocateBuffer(geom_count, index_count, TRUE);
 			}
@@ -3782,15 +4477,18 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 		U16 index_offset = 0;
 
 		while (face_iter < i)
-		{
+		{ //update face indices for new buffer
 			facep = *face_iter;
-			facep->mIndicesIndex = indices_index;
-			facep->mGeomIndex = index_offset;
-			facep->mVertexBuffer = buffer;
+			facep->setIndicesIndex(indices_index);
+			facep->setGeomIndex(index_offset);
+			facep->setVertexBuffer(buffer);	
+			
 			{
+				//for debugging, set last time face was updated vs moved
 				facep->updateRebuildFlags();
+
 				if (!LLPipeline::sDelayVBUpdate)
-				{
+				{ //copy face geometry into vertex buffer
 					LLDrawable* drawablep = facep->getDrawable();
 					LLVOVolume* vobj = drawablep->getVOVolume();
 					LLVolume* volume = vobj->getVolume();
@@ -3807,9 +4505,12 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 			}
 
 			index_offset += facep->getGeomCount();
-			indices_index += facep->mIndicesCount;
+			indices_index += facep->getIndicesCount();
+
 
-			BOOL force_simple = facep->mPixelArea < FORCE_SIMPLE_RENDER_AREA;
+			//append face to appropriate render batch
+
+			BOOL force_simple = facep->getPixelArea() < FORCE_SIMPLE_RENDER_AREA;
 			BOOL fullbright = facep->isState(LLFace::FULLBRIGHT);
 			if ((mask & LLVertexBuffer::MAP_NORMAL) == 0)
 			{ //paranoia check to make sure GL doesn't try to read non-existant normals
@@ -3825,7 +4526,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 				// can we safely treat this as an alpha mask?
 				if (facep->canRenderAsMask())
 				{
-					if (te->getFullbright())
+					if (te->getFullbright() || LLPipeline::sNoAlpha)
 					{
 						registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK);
 					}
@@ -3889,7 +4590,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 				{ //invisiprim
 					registerFace(group, facep, LLRenderPass::PASS_INVISIBLE);
 				}
-				else if (fullbright)
+				else if (fullbright || bake_sunlight)
 				{ //fullbright
 					registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT);
 					if (LLPipeline::sRenderDeferred && LLPipeline::sRenderBump && te->getBumpmap())
@@ -3976,7 +4677,7 @@ void LLGeometryManager::addGeometryCount(LLSpatialGroup* group, U32 &vertex_coun
 			//sum up face verts and indices
 			drawablep->updateFaceSize(i);
 			LLFace* facep = drawablep->getFace(i);
-			if (facep->hasGeometry() && facep->mPixelArea > FORCE_CULL_AREA)
+			if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
 			{
 				vertex_count += facep->getGeomCount();
 				index_count += facep->getIndicesCount();
@@ -3986,8 +4687,7 @@ void LLGeometryManager::addGeometryCount(LLSpatialGroup* group, U32 &vertex_coun
 			}
 			else
 			{
-				facep->mVertexBuffer = NULL;
-				facep->mLastVertexBuffer = NULL;
+				facep->clearVertexBuffer();
 			}
 		}
 	}
@@ -4003,7 +4703,7 @@ LLHUDPartition::LLHUDPartition()
 	mLODPeriod = 1;
 }
 
-void LLHUDPartition::shift(const LLVector3 &offset)
+void LLHUDPartition::shift(const LLVector4a &offset)
 {
 	//HUD objects don't shift with region crossing.  That would be silly.
 }
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 8b68e7c78a109cfa216b0dd54cd72cea30026539..fc00f0c0d0da3bcd6830e8597c6a104393fc7f5c 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -40,6 +40,8 @@ class LLDrawPool;
 class LLSelectNode;
 class LLObjectMediaDataClient;
 class LLObjectMediaNavigateClient;
+class LLVOAvatar;
+class LLMeshSkinInfo;
 
 typedef std::vector<viewer_media_t> media_list_t;
 
@@ -48,6 +50,18 @@ enum LLVolumeInterfaceType
 	INTERFACE_FLEXIBLE = 1,
 };
 
+
+class LLRiggedVolume : public LLVolume
+{
+public:
+	LLRiggedVolume(const LLVolumeParams& params)
+		: LLVolume(params, 0.f)
+	{
+	}
+
+	void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume);
+};
+
 // Base class for implementations of the volume - Primitive, Flexible Object, etc.
 class LLVolumeInterface
 {
@@ -60,7 +74,7 @@ class LLVolumeInterface
 	virtual void onSetVolume(const LLVolumeParams &volume_params, const S32 detail) = 0;
 	virtual void onSetScale(const LLVector3 &scale, BOOL damped) = 0;
 	virtual void onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin) = 0;
-	virtual void onShift(const LLVector3 &shift_vector) = 0;
+	virtual void onShift(const LLVector4a &shift_vector) = 0;
 	virtual bool isVolumeUnique() const = 0; // Do we need a unique LLVolume instance?
 	virtual bool isVolumeGlobal() const = 0; // Are we in global space?
 	virtual bool isActive() const = 0; // Is this object currently active?
@@ -116,7 +130,9 @@ class LLVOVolume : public LLViewerObject
 	const LLMatrix3&	getRelativeXformInvTrans() const		{ return mRelativeXformInvTrans; }
 	/*virtual*/	const LLMatrix4	getRenderMatrix() const;
 				U32 	getRenderCost(std::set<LLUUID> &textures) const;
-
+	/*virtual*/	F32		getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL);
+	/*virtual*/ U32		getTriangleCount();
+	/*virtual*/ U32		getHighLODTriangleCount();
 	/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
 										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
 										  BOOL pick_transparent = FALSE,
@@ -140,7 +156,7 @@ class LLVOVolume : public LLViewerObject
 
 				void	markForUpdate(BOOL priority)			{ LLViewerObject::markForUpdate(priority); mVolumeChanged = TRUE; }
 
-	/*virtual*/ void	onShift(const LLVector3 &shift_vector); // Called when the drawable shifts
+	/*virtual*/ void	onShift(const LLVector4a &shift_vector); // Called when the drawable shifts
 
 	/*virtual*/ void	parameterChanged(U16 param_type, bool local_origin);
 	/*virtual*/ void	parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin);
@@ -179,6 +195,11 @@ class LLVOVolume : public LLViewerObject
 				void	updateSculptTexture();
 				void    setIndexInTex(S32 index) { mIndexInTex = index ;}
 				void	sculpt();
+	 static     void    rebuildMeshAssetCallback(LLVFS *vfs,
+														  const LLUUID& asset_uuid,
+														  LLAssetType::EType type,
+														  void* user_data, S32 status, LLExtStat ext_status);
+					
 				void	updateRelativeXform();
 	/*virtual*/ BOOL	updateGeometry(LLDrawable *drawable);
 	/*virtual*/ void	updateFaceSize(S32 idx);
@@ -191,7 +212,7 @@ class LLVOVolume : public LLViewerObject
 				void	regenFaces();
 				BOOL	genBBoxes(BOOL force_global);
 				void	preRebuild();
-	virtual		void	updateSpatialExtents(LLVector3& min, LLVector3& max);
+	virtual		void	updateSpatialExtents(LLVector4a& min, LLVector4a& max);
 	virtual		F32		getBinRadius();
 	
 	virtual U32 getPartitionType() const;
@@ -225,6 +246,7 @@ class LLVOVolume : public LLViewerObject
 	U32 getVolumeInterfaceID() const;
 	virtual BOOL isFlexible() const;
 	virtual BOOL isSculpted() const;
+	virtual BOOL isMesh() const;
 	virtual BOOL hasLightTexture() const;
 
 	BOOL isVolumeGlobal() const;
@@ -249,6 +271,7 @@ class LLVOVolume : public LLViewerObject
     
 	void mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location);
 	void mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event);
+			
 
 	// Sync the given media data with the impl and the given te
 	void syncMediaData(S32 te, const LLSD &media_data, bool merge, bool ignore_agent);
@@ -264,9 +287,11 @@ class LLVOVolume : public LLViewerObject
 	
 	LLVector3 getApproximateFaceNormal(U8 face_id);
 	
+	void notifyMeshLoaded();
+	
 	// Returns 'true' iff the media data for this object is in flight
 	bool isMediaDataBeingFetched() const;
-	
+
 	// Returns the "last fetched" media version, or -1 if not fetched yet
 	S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; }
 
@@ -274,6 +299,21 @@ class LLVOVolume : public LLViewerObject
 	void removeMDCImpl() { --mMDCImplCount; }
 	S32 getMDCImplCount() { return mMDCImplCount; }
 	
+
+	//rigged volume update (for raycasting)
+	void updateRiggedVolume();
+	LLRiggedVolume* getRiggedVolume();
+
+	//returns true if volume should be treated as a rigged volume
+	// - Build tools are open
+	// - object is an attachment
+	// - object is attached to self
+	// - object is rendered as rigged
+	bool treatAsRigged();
+
+	//clear out rigged volume and revert back to non-rigged state for picking/LOD/distance updates
+	void clearRiggedVolume();
+
 protected:
 	S32	computeLODDetail(F32	distance, F32 radius);
 	BOOL calcLOD();
@@ -307,6 +347,9 @@ class LLVOVolume : public LLViewerObject
 	S32			mLastFetchedMediaVersion; // as fetched from the server, starts as -1
 	S32 mIndexInTex;
 	S32 mMDCImplCount;
+
+	LLPointer<LLRiggedVolume> mRiggedVolume;
+
 	// statics
 public:
 	static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop 
diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp
index 71f08ec36d7dfcee856a86d1919fa9c5f81cecb5..69ebad61ac49cb1fd7d8a85b04e953efb7fbac39 100644
--- a/indra/newview/llvowater.cpp
+++ b/indra/newview/llvowater.cpp
@@ -166,16 +166,18 @@ BOOL LLVOWater::updateGeometry(LLDrawable *drawable)
 	face->setSize(vertices_per_quad * num_quads,
 				  indices_per_quad * num_quads);
 	
-	if (face->mVertexBuffer.isNull())
+	LLVertexBuffer* buff = face->getVertexBuffer();
+	if (!buff)
 	{
-		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_DYNAMIC_DRAW_ARB);
-		face->mVertexBuffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
+		buff = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_DYNAMIC_DRAW_ARB);
+		buff->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
 		face->setIndicesIndex(0);
 		face->setGeomIndex(0);
+		face->setVertexBuffer(buff);
 	}
 	else
 	{
-		face->mVertexBuffer->resizeBuffer(face->getGeomCount(), face->getIndicesCount());
+		buff->resizeBuffer(face->getGeomCount(), face->getIndicesCount());
 	}
 		
 	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
@@ -229,7 +231,7 @@ BOOL LLVOWater::updateGeometry(LLDrawable *drawable)
 		}
 	}
 	
-	face->mVertexBuffer->setBuffer(0);
+	buff->setBuffer(0);
 
 	mDrawable->movePartition();
 	LLPipeline::sCompiles++;
@@ -261,15 +263,21 @@ void LLVOWater::setIsEdgePatch(const BOOL edge_patch)
 	mIsEdgePatch = edge_patch;
 }
 
-void LLVOWater::updateSpatialExtents(LLVector3 &newMin, LLVector3& newMax)
+void LLVOWater::updateSpatialExtents(LLVector4a &newMin, LLVector4a& newMax)
 {
-	LLVector3 pos = getPositionAgent();
-	LLVector3 scale = getScale();
-
-	newMin = pos - scale * 0.5f;
-	newMax = pos + scale * 0.5f;
+	LLVector4a pos;
+	pos.load3(getPositionAgent().mV);
+	LLVector4a scale;
+	scale.load3(getScale().mV);
+	scale.mul(0.5f);
+
+	newMin.setSub(pos, scale);
+	newMax.setAdd(pos, scale);
+	
+	pos.setAdd(newMin,newMax);
+	pos.mul(0.5f);
 
-	mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+	mDrawable->setPositionGroup(pos);
 }
 
 U32 LLVOWater::getPartitionType() const
@@ -283,7 +291,7 @@ U32 LLVOVoidWater::getPartitionType() const
 }
 
 LLWaterPartition::LLWaterPartition()
-: LLSpatialPartition(0, FALSE, 0)
+: LLSpatialPartition(0, FALSE, GL_DYNAMIC_DRAW_ARB)
 {
 	mInfiniteFarClip = TRUE;
 	mDrawableType = LLPipeline::RENDER_TYPE_WATER;
diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h
index cb9584cabf6e7292da48f95dbabe00f9d0e638d2..ed709dd84017c7ea69441e5e9a65426c55724ac3 100644
--- a/indra/newview/llvowater.h
+++ b/indra/newview/llvowater.h
@@ -61,7 +61,7 @@ class LLVOWater : public LLStaticViewerObject
 	/*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL        updateGeometry(LLDrawable *drawable);
-	/*virtual*/ void		updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+	/*virtual*/ void		updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
 
 	/*virtual*/ void updateTextures();
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp
index ca57c0144ba4616adf9cd2fd9b1fc28278da8d89..51664cb31d943a7f4101b315836f61e6d1feeadf 100644
--- a/indra/newview/llvowlsky.cpp
+++ b/indra/newview/llvowlsky.cpp
@@ -332,7 +332,7 @@ BOOL LLVOWLSky::updateGeometry(LLDrawable * drawable)
 	{
 		const U32 max_buffer_bytes = gSavedSettings.getS32("RenderMaxVBOSize")*1024;
 		const U32 data_mask = LLDrawPoolWLSky::SKY_VERTEX_DATA_MASK;
-		const U32 max_verts = max_buffer_bytes / LLVertexBuffer::calcStride(data_mask);
+		const U32 max_verts = max_buffer_bytes / LLVertexBuffer::calcVertexSize(data_mask);
 
 		const U32 total_stacks = getNumStacks();
 
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index b060c9f076cf8f67c55434975505f4477461b302..a8a5ef311781dff230fb936a6a02c5d6f119ffa9 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -396,7 +396,6 @@ class LLWearableItemCreationDateComparator : public LLWearableItemNameComparator
  */
 class LLWearableItemsList : public LLInventoryItemsList
 {
-	LOG_CLASS(LLWearableItemsList);
 public:
 	/**
 	 * Context menu.
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index fd42058c8ab8a6c3ca283d80211c1cc02696c4e2..ec24b029347a381015d81d76117aaf0d2ffa53cc 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -597,7 +597,7 @@ void LLWorld::updateVisibilities()
 		region_list_t::iterator curiter = iter++;
 		LLViewerRegion* regionp = *curiter;
 		F32 height = regionp->getLand().getMaxZ() - regionp->getLand().getMinZ();
-		F32 radius = 0.5f*fsqrtf(height * height + diagonal_squared);
+		F32 radius = 0.5f*(F32) sqrt(height * height + diagonal_squared);
 		if (!regionp->getLand().hasZData()
 			|| LLViewerCamera::getInstance()->sphereInFrustum(regionp->getCenterAgent(), radius))
 		{
@@ -618,7 +618,7 @@ void LLWorld::updateVisibilities()
 		}
 
 		F32 height = regionp->getLand().getMaxZ() - regionp->getLand().getMinZ();
-		F32 radius = 0.5f*fsqrtf(height * height + diagonal_squared);
+		F32 radius = 0.5f*(F32) sqrt(height * height + diagonal_squared);
 		if (LLViewerCamera::getInstance()->sphereInFrustum(regionp->getCenterAgent(), radius))
 		{
 			regionp->calculateCameraDistance();
@@ -867,42 +867,6 @@ void LLWorld::waterHeightRegionInfo(std::string const& sim_name, F32 water_heigh
 	}
 }
 
-// There are three types of water objects:
-// Region water objects: the water in a region.
-// Hole water objects: water in the void but within current draw distance.
-// Edge water objects: the water outside the draw distance, up till the horizon.
-//
-// For example:
-//
-// -----------------------horizon-------------------------
-// |                 |                 |                 |
-// |  Edge Water     |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |      rwidth     |                 |
-// |                 |     <----->     |                 |
-// -------------------------------------------------------
-// |                 |Hole |other|     |                 |
-// |                 |Water|reg. |     |                 |
-// |                 |-----------------|                 |
-// |                 |other|cur. |<--> |                 |
-// |                 |reg. | reg.|  \__|_ draw distance  |
-// |                 |-----------------|                 |
-// |                 |     |     |<--->|                 |
-// |                 |     |     |  \__|_ range          |
-// -------------------------------------------------------
-// |                 |<----width------>|<--horizon ext.->|
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// |                 |                 |                 |
-// -------------------------------------------------------
-//
 void LLWorld::updateWaterObjects()
 {
 	if (!gAgent.getRegion())
@@ -915,265 +879,128 @@ void LLWorld::updateWaterObjects()
 		return;
 	}
 
-	// Region width in meters.
-	S32 const rwidth = (S32)REGION_WIDTH_U32;
+	// First, determine the min and max "box" of water objects
+	S32 min_x = 0;
+	S32 min_y = 0;
+	S32 max_x = 0;
+	S32 max_y = 0;
+	U32 region_x, region_y;
 
-	// The distance we might see into the void
-	// when standing on the edge of a region, in meters.
-	S32 const draw_distance = llceil(mLandFarClip);
+	S32 rwidth = 256;
 
-	// We can only have "holes" in the water (where there no region) if we
-	// can have existing regions around it. Taking into account that this
-	// code is only executed when we enter a region, and not when we walk
-	// around in it, we (only) need to take into account regions that fall
-	// within the draw_distance.
-	//
-	// Set 'range' to draw_distance, rounded up to the nearest multiple of rwidth.
-	S32 const nsims = (draw_distance + rwidth - 1) / rwidth;
-	S32 const range = nsims * rwidth;
+	// We only want to fill in water for stuff that's near us, say, within 256 or 512m
+	S32 range = LLViewerCamera::getInstance()->getFar() > 256.f ? 512 : 256;
 
-	// Get South-West corner of current region.
-	LLViewerRegion const* regionp = gAgent.getRegion();
-	U32 region_x, region_y;
+	LLViewerRegion* regionp = gAgent.getRegion();
 	from_region_handle(regionp->getHandle(), &region_x, &region_y);
 
-	// The min. and max. coordinates of the South-West corners of the Hole water objects.
-	S32 const min_x = (S32)region_x - range;
-	S32 const min_y = (S32)region_y - range;
-	S32 const max_x = (S32)region_x + range;
-	S32 const max_y = (S32)region_y + range;
-
-	// Attempt to determine a sensible water height for all the
-	// Hole Water objects.
-	//
-	// It make little sense to try to guess what the best water
-	// height should be when that isn't completely obvious: if it's
-	// impossible to satisfy every region's water height without
-	// getting a jump in the water height.
-	//
-	// In order to keep the reasoning simple, we assume something
-	// logical as a group of connected regions, where the coastline
-	// is at the outer edge. Anything more complex that would "break"
-	// under such an assumption would probably break anyway (would
-	// depend on terrain editing and existing mega prims, say, if
-	// anything would make sense at all).
-	//
-	// So, what we do is find all connected regions within the
-	// draw distance that border void, and then pick the lowest
-	// water height of those (coast) regions.
-	S32 const n = 2 * nsims + 1;
-	S32 const origin = nsims + nsims * n;
-	std::vector<F32> water_heights(n * n);
-	std::vector<U8> checked(n * n, 0);		// index = nx + ny * n + origin;
-	U8 const region_bit = 1;
-	U8 const hole_bit = 2;
-	U8 const bordering_hole_bit = 4;
-	U8 const bordering_edge_bit = 8;
-	// Use the legacy waterheight for the Edge water in the case
-	// that we don't find any Hole water at all.
-	F32 water_height = DEFAULT_WATER_HEIGHT;
-	int max_count = 0;
-	LL_DEBUGS("WaterHeight") << "Current region: " << regionp->getName() << "; water height: " << regionp->getWaterHeight() << " m." << LL_ENDL;
-	std::map<S32, int> water_height_counts;
-	typedef std::queue<std::pair<S32, S32>, std::deque<std::pair<S32, S32> > > nxny_pairs_type;
-	nxny_pairs_type nxny_pairs;
-	nxny_pairs.push(nxny_pairs_type::value_type(0, 0));
-	water_heights[origin] = regionp->getWaterHeight();
-	checked[origin] = region_bit;
-	// For debugging purposes.
-	int number_of_connected_regions = 1;
-	int uninitialized_regions = 0;
-	int bordering_hole = 0;
-	int bordering_edge = 0;
-	while(!nxny_pairs.empty())
-	{
-		S32 const nx = nxny_pairs.front().first;
-		S32 const ny = nxny_pairs.front().second;
-		LL_DEBUGS("WaterHeight") << "nx,ny = " << nx << "," << ny << LL_ENDL;
-		S32 const index = nx + ny * n + origin;
-		nxny_pairs.pop();
-		for (S32 dir = 0; dir < 4; ++dir)
-		{
-			S32 const cnx = nx + gDirAxes[dir][0];
-			S32 const cny = ny + gDirAxes[dir][1];
-			LL_DEBUGS("WaterHeight") << "dir = " << dir << "; cnx,cny = " << cnx << "," << cny << LL_ENDL;
-			S32 const cindex = cnx + cny * n + origin;
-			bool is_hole = false;
-			bool is_edge = false;
-			LLViewerRegion* new_region_found = NULL;
-			if (cnx < -nsims || cnx > nsims ||
-			    cny < -nsims || cny > nsims)
-			{
-				LL_DEBUGS("WaterHeight") << "  Edge Water!" << LL_ENDL;
-				// Bumped into Edge water object.
-				is_edge = true;
-			}
-			else if (checked[cindex])
-			{
-				LL_DEBUGS("WaterHeight") << "  Already checked before!" << LL_ENDL;
-				// Already checked.
-				is_hole = (checked[cindex] & hole_bit);
-			}
-			else
-			{
-				S32 x = (S32)region_x + cnx * rwidth;
-				S32 y = (S32)region_y + cny * rwidth;
-				U64 region_handle = to_region_handle(x, y);
-				new_region_found = getRegionFromHandle(region_handle);
-				is_hole = !new_region_found;
-				checked[cindex] = is_hole ? hole_bit : region_bit;
-			}
-			if (is_hole)
-			{
-				// This was a region that borders at least one 'hole'.
-				// Count the found coastline.
-				F32 new_water_height = water_heights[index];
-				LL_DEBUGS("WaterHeight") << "  This is void; counting coastline with water height of " << new_water_height << LL_ENDL;
-				S32 new_water_height_cm = llround(new_water_height * 100);
-				int count = (water_height_counts[new_water_height_cm] += 1);
-				// Just use the lowest water height: this is mainly about the horizon water,
-				// and whatever we do, we don't want it to be possible to look under the water
-				// when looking in the distance: it is better to make a step downwards in water
-				// height when going away from the avie than a step upwards. However, since
-				// everyone is used to DEFAULT_WATER_HEIGHT, don't allow a single region
-				// to drag the water level below DEFAULT_WATER_HEIGHT on it's own.
-				if (bordering_hole == 0 ||			// First time we get here.
-				    (new_water_height >= DEFAULT_WATER_HEIGHT &&
-					 new_water_height < water_height) ||
-				    (new_water_height < DEFAULT_WATER_HEIGHT &&
-					 count > max_count)
-				   )
-				{
-					water_height = new_water_height;
-				}
-				if (count > max_count)
-				{
-					max_count = count;
-				}
-				if (!(checked[index] & bordering_hole_bit))
-				{
-					checked[index] |= bordering_hole_bit;
-					++bordering_hole;
-				}
-			}
-			else if (is_edge && !(checked[index] & bordering_edge_bit))
-			{
-				checked[index] |= bordering_edge_bit;
-				++bordering_edge;
-			}
-			if (!new_region_found)
-			{
-				// Dead end, there is no region here.
-				continue;
-			}
-			// Found a new connected region.
-			++number_of_connected_regions;
-			if (new_region_found->getName().empty())
-			{
-				// Uninitialized LLViewerRegion, don't use it's water height.
-				LL_DEBUGS("WaterHeight") << "  Uninitialized region." << LL_ENDL;
-				++uninitialized_regions;
-				continue;
-			}
-			nxny_pairs.push(nxny_pairs_type::value_type(cnx, cny));
-			water_heights[cindex] = new_region_found->getWaterHeight();
-			LL_DEBUGS("WaterHeight") << "  Found a new region (name: " << new_region_found->getName() << "; water height: " << water_heights[cindex] << " m)!" << LL_ENDL;
-		}
-	}
-	llinfos << "Number of connected regions: " << number_of_connected_regions << " (" << uninitialized_regions <<
-		" uninitialized); number of regions bordering Hole water: " << bordering_hole <<
-		"; number of regions bordering Edge water: " << bordering_edge << llendl;
-	llinfos << "Coastline count (height, count): ";
-	bool first = true;
-	for (std::map<S32, int>::iterator iter = water_height_counts.begin(); iter != water_height_counts.end(); ++iter)
-	{
-		if (!first) llcont << ", ";
-		llcont << "(" << (iter->first / 100.f) << ", " << iter->second << ")";
-		first = false;
-	}
-	llcont << llendl;
-	llinfos << "Water height used for Hole and Edge water objects: " << water_height << llendl;
+	min_x = (S32)region_x - range;
+	min_y = (S32)region_y - range;
+	max_x = (S32)region_x + range;
+	max_y = (S32)region_y + range;
 
-	// Update all Region water objects.
-	for (region_list_t::iterator iter = mRegionList.begin(); iter != mRegionList.end(); ++iter)
+	F32 height = 0.f;
+	
+	for (region_list_t::iterator iter = mRegionList.begin();
+		 iter != mRegionList.end(); ++iter)
 	{
 		LLViewerRegion* regionp = *iter;
 		LLVOWater* waterp = regionp->getLand().getWaterObj();
+		height += regionp->getWaterHeight();
 		if (waterp)
 		{
 			gObjectList.updateActive(waterp);
 		}
 	}
 
-	// Clean up all existing Hole water objects.
 	for (std::list<LLVOWater*>::iterator iter = mHoleWaterObjects.begin();
-		 iter != mHoleWaterObjects.end(); ++iter)
+		 iter != mHoleWaterObjects.end(); ++ iter)
 	{
 		LLVOWater* waterp = *iter;
 		gObjectList.killObject(waterp);
 	}
 	mHoleWaterObjects.clear();
 
-	// Let the Edge and Hole water boxes be 1024 meter high so that they
-	// are never too small to be drawn (A LL_VO_*_WATER box has water
-	// rendered on it's bottom surface only), and put their bottom at
-	// the current regions water height.
-	F32 const box_height = 1024;
-	F32 const water_center_z = water_height + box_height / 2;
-
-	// Create new Hole water objects within 'range' where there is no region.
-	for (S32 x = min_x; x <= max_x; x += rwidth)
+	// Now, get a list of the holes
+	S32 x, y;
+	for (x = min_x; x <= max_x; x += rwidth)
 	{
-		for (S32 y = min_y; y <= max_y; y += rwidth)
+		for (y = min_y; y <= max_y; y += rwidth)
 		{
 			U64 region_handle = to_region_handle(x, y);
 			if (!getRegionFromHandle(region_handle))
 			{
-				LLVOWater* waterp = (LLVOWater*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_VOID_WATER, gAgent.getRegion());
+				LLVOWater* waterp = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, gAgent.getRegion());
 				waterp->setUseTexture(FALSE);
-				waterp->setPositionGlobal(LLVector3d(x + rwidth / 2, y + rwidth / 2, water_center_z));
-				waterp->setScale(LLVector3((F32)rwidth, (F32)rwidth, box_height));
+				waterp->setPositionGlobal(LLVector3d(x + rwidth/2,
+													 y + rwidth/2,
+													 256.f+DEFAULT_WATER_HEIGHT));
+				waterp->setScale(LLVector3((F32)rwidth, (F32)rwidth, 512.f));
 				gPipeline.createObject(waterp);
 				mHoleWaterObjects.push_back(waterp);
 			}
 		}
 	}
 
-	// Center of the region.
-	S32 const center_x = region_x + rwidth / 2;
-	S32 const center_y = region_y + rwidth / 2;
-	// Width of the area with Hole water objects.
-	S32 const width = rwidth + 2 * range;
-	S32 const horizon_extend = 2048 + 512 - range;	// Legacy value.
-	// The overlap is needed to get rid of sky pixels being visible between the
-	// Edge and Hole water object at greater distances (due to floating point
-	// round off errors).
-	S32 const edge_hole_overlap = 1;		// Twice the actual overlap.
+	// Update edge water objects
+	S32 wx, wy;
+	S32 center_x, center_y;
+	wx = (max_x - min_x) + rwidth;
+	wy = (max_y - min_y) + rwidth;
+	center_x = min_x + (wx >> 1);
+	center_y = min_y + (wy >> 1);
+
+	S32 add_boundary[4] = {
+		512 - (max_x - region_x),
+		512 - (max_y - region_y),
+		512 - (region_x - min_x),
+		512 - (region_y - min_y) };
 		
-	for (S32 dir = 0; dir < 8; ++dir)
+	S32 dir;
+	for (dir = 0; dir < 8; dir++)
 	{
-		// Size of the Edge water objects.
-		S32 const dim_x = (gDirAxes[dir][0] == 0) ? width : (horizon_extend + edge_hole_overlap);
-		S32 const dim_y = (gDirAxes[dir][1] == 0) ? width : (horizon_extend + edge_hole_overlap);
-		// And their position.
-		S32 const water_center_x = center_x + (width + horizon_extend) / 2 * gDirAxes[dir][0];
-		S32 const water_center_y = center_y + (width + horizon_extend) / 2 * gDirAxes[dir][1];
+		S32 dim[2] = { 0 };
+		switch (gDirAxes[dir][0])
+		{
+		case -1: dim[0] = add_boundary[2]; break;
+		case  0: dim[0] = wx; break;
+		default: dim[0] = add_boundary[0]; break;
+		}
+		switch (gDirAxes[dir][1])
+		{
+		case -1: dim[1] = add_boundary[3]; break;
+		case  0: dim[1] = wy; break;
+		default: dim[1] = add_boundary[1]; break;
+		}
 
+		// Resize and reshape the water objects
+		const S32 water_center_x = center_x + llround((wx + dim[0]) * 0.5f * gDirAxes[dir][0]);
+		const S32 water_center_y = center_y + llround((wy + dim[1]) * 0.5f * gDirAxes[dir][1]);
+		
 		LLVOWater* waterp = mEdgeWaterObjects[dir];
 		if (!waterp || waterp->isDead())
 		{
 			// The edge water objects can be dead because they're attached to the region that the
 			// agent was in when they were originally created.
-			mEdgeWaterObjects[dir] = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_VOID_WATER, gAgent.getRegion());
+			mEdgeWaterObjects[dir] = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_VOID_WATER,
+																				 gAgent.getRegion());
 			waterp = mEdgeWaterObjects[dir];
 			waterp->setUseTexture(FALSE);
-			waterp->setIsEdgePatch(TRUE);		// Mark that this is edge water and not hole water.
+			waterp->setIsEdgePatch(TRUE);
 			gPipeline.createObject(waterp);
 		}
 
 		waterp->setRegion(gAgent.getRegion());
-		LLVector3d water_pos(water_center_x, water_center_y, water_center_z);
-		LLVector3 water_scale((F32) dim_x, (F32) dim_y, box_height);
+		LLVector3d water_pos(water_center_x, water_center_y, 
+			DEFAULT_WATER_HEIGHT+256.f);
+		LLVector3 water_scale((F32) dim[0], (F32) dim[1], 512.f);
+
+		//stretch out to horizon
+		water_scale.mV[0] += fabsf(2048.f * gDirAxes[dir][0]);
+		water_scale.mV[1] += fabsf(2048.f * gDirAxes[dir][1]);
+
+		water_pos.mdV[0] += 1024.f * gDirAxes[dir][0];
+		water_pos.mdV[1] += 1024.f * gDirAxes[dir][1];
 
 		waterp->setPositionGlobal(water_pos);
 		waterp->setScale(water_scale);
@@ -1182,6 +1009,7 @@ void LLWorld::updateWaterObjects()
 	}
 }
 
+
 void LLWorld::shiftRegions(const LLVector3& offset)
 {
 	for (region_list_t::const_iterator i = getRegionList().begin(); i != getRegionList().end(); ++i)
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 6dc8f28265950f49b73893f4965d577583b2f394..f64eb8986691799addb47364501ca5d81027bad3 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -67,6 +67,7 @@
 #include "llhudnametag.h"
 #include "llhudtext.h"
 #include "lllightconstants.h"
+#include "llmeshrepository.h"
 #include "llresmgr.h"
 #include "llselectmgr.h"
 #include "llsky.h"
@@ -74,6 +75,7 @@
 #include "lltool.h"
 #include "lltoolmgr.h"
 #include "llviewercamera.h"
+#include "llviewermediafocus.h"
 #include "llviewertexturelist.h"
 #include "llviewerobject.h"
 #include "llviewerobjectlist.h"
@@ -100,8 +102,29 @@
 #include "llspatialpartition.h"
 #include "llmutelist.h"
 #include "lltoolpie.h"
+#include "llcurl.h"
 
 
+void check_stack_depth(S32 stack_depth)
+{
+	if (gDebugGL || gDebugSession)
+	{
+		GLint depth;
+		glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth);
+		if (depth != stack_depth)
+		{
+			if (gDebugSession)
+			{
+				ll_fail("GL matrix stack corrupted.");
+			}
+			else
+			{
+				llerrs << "GL matrix stack corrupted!" << llendl;
+			}
+		}
+	}
+}
+	
 #ifdef _DEBUG
 // Debug indices is disabled for now for debug performance - djs 4/24/02
 //#define DEBUG_INDICES
@@ -169,19 +192,20 @@ std::string gPoolNames[] =
 	// Correspond to LLDrawpool enum render type
 	"NONE",
 	"POOL_SIMPLE",
-	"POOL_TERRAIN",
+	"POOL_GROUND",
+	"POOL_FULLBRIGHT",
 	"POOL_BUMP",
-	"POOL_TREE",
+	"POOL_TERRAIN,"	
 	"POOL_SKY",
 	"POOL_WL_SKY",
-	"POOL_GROUND",
+	"POOL_TREE",
+	"POOL_GRASS",
 	"POOL_INVISIBLE",
 	"POOL_AVATAR",
+	"POOL_VOIDWATER",
 	"POOL_WATER",
-	"POOL_GRASS",
-	"POOL_FULLBRIGHT",
 	"POOL_GLOW",
-	"POOL_ALPHA",
+	"POOL_ALPHA"
 };
 
 void drawBox(const LLVector3& c, const LLVector3& r);
@@ -216,6 +240,16 @@ glh::matrix4f glh_get_current_projection()
 	return glh_copy_matrix(gGLProjection);
 }
 
+glh::matrix4f glh_get_last_modelview()
+{
+	return glh_copy_matrix(gGLLastModelView);
+}
+
+glh::matrix4f glh_get_last_projection()
+{
+	return glh_copy_matrix(gGLLastProjection);
+}
+
 void glh_copy_matrix(const glh::matrix4f& src, GLdouble* dst)
 {
 	for (U32 i = 0; i < 16; i++)
@@ -268,6 +302,8 @@ BOOL	LLPipeline::sAutoMaskAlphaDeferred = TRUE;
 BOOL	LLPipeline::sAutoMaskAlphaNonDeferred = FALSE;
 BOOL	LLPipeline::sDisableShaders = FALSE;
 BOOL	LLPipeline::sRenderBump = TRUE;
+BOOL	LLPipeline::sBakeSunlight = FALSE;
+BOOL	LLPipeline::sNoAlpha = FALSE;
 BOOL	LLPipeline::sUseTriStrips = TRUE;
 BOOL	LLPipeline::sUseFarClip = TRUE;
 BOOL	LLPipeline::sShadowRender = FALSE;
@@ -281,7 +317,6 @@ BOOL	LLPipeline::sRenderFrameTest = FALSE;
 BOOL	LLPipeline::sRenderAttachedLights = TRUE;
 BOOL	LLPipeline::sRenderAttachedParticles = TRUE;
 BOOL	LLPipeline::sRenderDeferred = FALSE;
-BOOL    LLPipeline::sAllowRebuildPriorityGroup = FALSE ;
 S32		LLPipeline::sVisibleLightCount = 0;
 F32		LLPipeline::sMinRenderSize = 0.f;
 
@@ -328,6 +363,8 @@ LLPipeline::LLPipeline() :
 	mRenderDebugFeatureMask(0),
 	mRenderDebugMask(0),
 	mOldRenderDebugMask(0),
+	mGroupQ1Locked(false),
+	mGroupQ2Locked(false),
 	mLastRebuildPool(NULL),
 	mAlphaPool(NULL),
 	mSkyPool(NULL),
@@ -359,6 +396,7 @@ void LLPipeline::init()
 	sRenderBump = gSavedSettings.getBOOL("RenderObjectBump");
 	sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips");
 	LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO");
+	LLVertexBuffer::sPreferStreamDraw = gSavedSettings.getBOOL("RenderPreferStreamDraw");
 	sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights");
 	sRenderAttachedParticles = gSavedSettings.getBOOL("RenderAttachedParticles");
 
@@ -393,6 +431,14 @@ void LLPipeline::init()
 		toggleRenderType(RENDER_TYPE_GROUND);
 	}
 
+	// make sure RenderPerformanceTest persists (hackity hack hack)
+	// disables non-object rendering (UI, sky, water, etc)
+	if (gSavedSettings.getBOOL("RenderPerformanceTest"))
+	{
+		gSavedSettings.setBOOL("RenderPerformanceTest", FALSE);
+		gSavedSettings.setBOOL("RenderPerformanceTest", TRUE);
+	}
+
 	mOldRenderDebugMask = mRenderDebugMask;
 
 	mBackfaceCull = TRUE;
@@ -526,6 +572,22 @@ void LLPipeline::resizeScreenTexture()
 	}
 }
 
+void LLPipeline::allocatePhysicsBuffer()
+{
+	GLuint resX = gViewerWindow->getWorldViewWidthRaw();
+	GLuint resY = gViewerWindow->getWorldViewHeightRaw();
+
+	if (mPhysicsDisplay.getWidth() != resX || mPhysicsDisplay.getHeight() != resY)
+	{
+		mPhysicsDisplay.allocate(resX, resY, GL_RGBA, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
+		if (mSampleBuffer.getWidth() == mPhysicsDisplay.getWidth() && 
+			mSampleBuffer.getHeight() == mPhysicsDisplay.getHeight())
+		{
+			mPhysicsDisplay.setSampleBuffer(&mSampleBuffer);
+		}
+	}
+}
+
 void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
 {
 	// remember these dimensions
@@ -534,6 +596,11 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
 	
 	//never use more than 4 samples for render targets
 	U32 samples = llmin(gSavedSettings.getU32("RenderFSAASamples"), (U32) 4);
+	if (gGLManager.mIsATI)
+	{ //disable multisampling of render targets where ATI is involved
+		samples = 0;
+	}
+
 	U32 res_mod = gSavedSettings.getU32("RenderResolutionDivisor");
 
 	if (res_mod > 1 && res_mod < resX && res_mod < resY)
@@ -549,6 +616,10 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
 
 	if (LLPipeline::sRenderDeferred)
 	{
+		S32 shadow_detail = gSavedSettings.getS32("RenderShadowDetail");
+		BOOL ssao = gSavedSettings.getBOOL("RenderDeferredSSAO");
+		bool gi = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED);
+
 		//allocate deferred rendering color buffers
 		mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
 		mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
@@ -557,14 +628,40 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
 		mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);		
 		mEdgeMap.allocate(resX, resY, GL_ALPHA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
 
-		for (U32 i = 0; i < 3; i++)
+		if (shadow_detail > 0 || ssao)
+		{ //only need mDeferredLight[0] for shadows OR ssao
+			mDeferredLight[0].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+		}
+		else
 		{
-			mDeferredLight[i].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+			mDeferredLight[0].release();
 		}
 
-		for (U32 i = 0; i < 2; i++)
+		if (ssao)
+		{ //only need mDeferredLight[1] for ssao
+			mDeferredLight[1].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+		}
+		else
 		{
-			mGIMapPost[i].allocate(resX,resY, GL_RGB, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+			mDeferredLight[1].release();
+		}
+
+		if (gi)
+		{ //only need mDeferredLight[2] and mGIMapPost for gi
+			mDeferredLight[2].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+			for (U32 i = 0; i < 2; i++)
+			{
+				mGIMapPost[i].allocate(resX,resY, GL_RGB, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+			}
+		}
+		else
+		{
+			mDeferredLight[2].release();
+		
+			for (U32 i = 0; i < 2; i++)
+			{
+				mGIMapPost[i].release();
+			}
 		}
 
 		F32 scale = gSavedSettings.getF32("RenderShadowResolutionScale");
@@ -572,18 +669,37 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
 		//HACK: make alpha masking work on ATI depth shadows (work around for ATI driver bug)
 		U32 shadow_fmt = gGLManager.mIsATI ? GL_ALPHA : 0;
 
-		for (U32 i = 0; i < 4; i++)
+		if (shadow_detail > 0)
+		{ //allocate 4 sun shadow maps
+			for (U32 i = 0; i < 4; i++)
+			{
+				mShadow[i].allocate(U32(resX*scale),U32(resY*scale), shadow_fmt, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+			}
+		}
+		else
 		{
-			mShadow[i].allocate(U32(resX*scale),U32(resY*scale), shadow_fmt, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+			for (U32 i = 0; i < 4; i++)
+			{
+				mShadow[i].release();
+			}
 		}
 
-
 		U32 width = nhpo2(U32(resX*scale))/2;
 		U32 height = width;
 
-		for (U32 i = 4; i < 6; i++)
+		if (shadow_detail > 1)
+		{ //allocate two spot shadow maps
+			for (U32 i = 4; i < 6; i++)
+			{
+				mShadow[i].allocate(width, height, shadow_fmt, TRUE, FALSE);
+			}
+		}
+		else
 		{
-			mShadow[i].allocate(width, height, shadow_fmt, TRUE, FALSE);
+			for (U32 i = 4; i < 6; i++)
+			{
+				mShadow[i].release();
+			}
 		}
 
 		width = nhpo2(resX)/2;
@@ -592,30 +708,55 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
 	}
 	else
 	{
+		for (U32 i = 0; i < 3; i++)
+		{ 
+			mDeferredLight[i].release();
+		}
+		for (U32 i = 0; i < 2; i++)
+		{
+			mGIMapPost[i].release();
+		}
+		for (U32 i = 0; i < 6; i++)
+		{
+			mShadow[i].release();
+		}
+		mScreen.release();
+		mDeferredScreen.release(); //make sure to release any render targets that share a depth buffer with mDeferredScreen first
+		mDeferredDepth.release();
+		mEdgeMap.release();
+		mLuminanceMap.release();
+		
 		mScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE);		
 	}
 	
-
-	if (LLRenderTarget::sUseFBO && gGLManager.mHasFramebufferMultisample && samples > 1)
-	{
+	if (LLRenderTarget::sUseFBO && samples > 1)
+	{ 
 		mSampleBuffer.allocate(resX,resY,GL_RGBA,TRUE,TRUE,LLTexUnit::TT_RECT_TEXTURE,FALSE,samples);
 		if (LLPipeline::sRenderDeferred)
 		{
 			addDeferredAttachments(mSampleBuffer);
 			mDeferredScreen.setSampleBuffer(&mSampleBuffer);
+			mEdgeMap.setSampleBuffer(&mSampleBuffer);
 		}
 
 		mScreen.setSampleBuffer(&mSampleBuffer);
 
 		stop_glerror();
 	}
+	else
+	{
+		mSampleBuffer.release();
+	}
 	
 	if (LLPipeline::sRenderDeferred)
 	{ //share depth buffer between deferred targets
 		mDeferredScreen.shareDepthBuffer(mScreen);
 		for (U32 i = 0; i < 3; i++)
 		{ //share stencil buffer with screen space lightmap to stencil out sky
-			mDeferredScreen.shareDepthBuffer(mDeferredLight[i]);
+			if (mDeferredLight[i].getTexture(0))
+			{
+				mDeferredScreen.shareDepthBuffer(mDeferredLight[i]);
+			}
 		}
 	}
 
@@ -636,7 +777,26 @@ void LLPipeline::updateRenderDeferred()
 					 gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ? TRUE : FALSE) &&
 					!gUseWireframe;
 
-	sRenderDeferred = deferred;			
+	sRenderDeferred = deferred;	
+	if (deferred)
+	{ //must render glow when rendering deferred since post effect pass is needed to present any lighting at all
+		sRenderGlow = TRUE;
+	}
+}
+
+//static
+void LLPipeline::refreshRenderDeferred()
+{
+	if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PHYSICS_SHAPES))
+	{
+		//turn the deferred rendering and glow off when draw physics shapes.
+		sRenderDeferred = FALSE ;
+		sRenderGlow = FALSE ;
+	}
+	else
+	{
+		updateRenderDeferred() ;
+	}
 }
 
 void LLPipeline::releaseGLBuffers()
@@ -664,8 +824,9 @@ void LLPipeline::releaseGLBuffers()
 	mWaterRef.release();
 	mWaterDis.release();
 	mScreen.release();
+	mPhysicsDisplay.release();
 	mUIScreen.release();
-	mSampleBuffer.releaseSampleBuffer();
+	mSampleBuffer.release();
 	mDeferredScreen.release();
 	mDeferredDepth.release();
 	for (U32 i = 0; i < 3; i++)
@@ -690,6 +851,7 @@ void LLPipeline::releaseGLBuffers()
 		mGlow[i].release();
 	}
 
+	gBumpImageList.destroyGL();
 	LLVOAvatar::resetImpostors();
 }
 
@@ -728,7 +890,6 @@ void LLPipeline::createGLBuffers()
 		allocateScreenBuffer(resX,resY);
 		mScreenWidth = 0;
 		mScreenHeight = 0;
-
 	}
 	
 	if (sRenderDeferred)
@@ -813,6 +974,8 @@ void LLPipeline::createGLBuffers()
 			addDeferredAttachments(mGIMap);
 		}
 	}
+
+	gBumpImageList.restoreGL();
 }
 
 void LLPipeline::restoreGL() 
@@ -872,7 +1035,7 @@ BOOL LLPipeline::canUseWindLightShadersOnObjects() const
 
 BOOL LLPipeline::canUseAntiAliasing() const
 {
-	return TRUE; //(gSavedSettings.getBOOL("RenderUseFBO"));
+	return TRUE;
 }
 
 void LLPipeline::unloadShaders()
@@ -910,11 +1073,10 @@ S32 LLPipeline::getMaxLightingDetail() const
 S32 LLPipeline::setLightingDetail(S32 level)
 {
 	LLMemType mt_ld(LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL);
-	assertInitialized();
 
 	if (level < 0)
 	{
-		if (gSavedSettings.getBOOL("VertexShaderEnable"))
+		if (gSavedSettings.getBOOL("RenderLocalLights"))
 		{
 			level = 1;
 		}
@@ -924,15 +1086,8 @@ S32 LLPipeline::setLightingDetail(S32 level)
 		}
 	}
 	level = llclamp(level, 0, getMaxLightingDetail());
-	if (level != mLightingDetail)
-	{
-		mLightingDetail = level;
-
-		if (mVertexShadersLoaded == 1)
-		{
-			LLViewerShaderMgr::instance()->setShaders();
-		}
-	}
+	mLightingDetail = level;
+	
 	return mLightingDetail;
 }
 
@@ -1158,9 +1313,15 @@ void LLPipeline::allocDrawable(LLViewerObject *vobj)
 }
 
 
+static LLFastTimer::DeclareTimer FTM_UNLINK("Unlink");
+static LLFastTimer::DeclareTimer FTM_REMOVE_FROM_MOVE_LIST("Movelist");
+static LLFastTimer::DeclareTimer FTM_REMOVE_FROM_SPATIAL_PARTITION("Spatial Partition");
+static LLFastTimer::DeclareTimer FTM_REMOVE_FROM_LIGHT_SET("Light Set");
+static LLFastTimer::DeclareTimer FTM_REMOVE_FROM_HIGHLIGHT_SET("Highlight Set");
+
 void LLPipeline::unlinkDrawable(LLDrawable *drawable)
 {
-	LLFastTimer t(FTM_PIPELINE);
+	LLFastTimer t(FTM_UNLINK);
 
 	assertInitialized();
 
@@ -1169,6 +1330,7 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable)
 	// Based on flags, remove the drawable from the queues that it's on.
 	if (drawablep->isState(LLDrawable::ON_MOVE_LIST))
 	{
+		LLFastTimer t(FTM_REMOVE_FROM_MOVE_LIST);
 		LLDrawable::drawable_vector_t::iterator iter = std::find(mMovedList.begin(), mMovedList.end(), drawablep);
 		if (iter != mMovedList.end())
 		{
@@ -1178,6 +1340,7 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable)
 
 	if (drawablep->getSpatialGroup())
 	{
+		LLFastTimer t(FTM_REMOVE_FROM_SPATIAL_PARTITION);
 		if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup()))
 		{
 #ifdef LL_RELEASE_FOR_DOWNLOAD
@@ -1188,18 +1351,23 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable)
 		}
 	}
 
-	mLights.erase(drawablep);
-	for (light_set_t::iterator iter = mNearbyLights.begin();
-				iter != mNearbyLights.end(); iter++)
 	{
-		if (iter->drawable == drawablep)
+		LLFastTimer t(FTM_REMOVE_FROM_LIGHT_SET);
+		mLights.erase(drawablep);
+
+		for (light_set_t::iterator iter = mNearbyLights.begin();
+					iter != mNearbyLights.end(); iter++)
 		{
-			mNearbyLights.erase(iter);
-			break;
+			if (iter->drawable == drawablep)
+			{
+				mNearbyLights.erase(iter);
+				break;
+			}
 		}
 	}
 
 	{
+		LLFastTimer t(FTM_REMOVE_FROM_HIGHLIGHT_SET);
 		HighlightItem item(drawablep);
 		mHighlightSet.erase(item);
 
@@ -1492,11 +1660,214 @@ F32 LLPipeline::calcPixelArea(LLVector3 center, LLVector3 size, LLCamera &camera
 	return radius*radius * F_PI;
 }
 
+//static
+F32 LLPipeline::calcPixelArea(const LLVector4a& center, const LLVector4a& size, LLCamera &camera)
+{
+	LLVector4a origin;
+	origin.load3(camera.getOrigin().mV);
+
+	LLVector4a lookAt;
+	lookAt.setSub(center, origin);
+	F32 dist = lookAt.getLength3().getF32();
+
+	//ramp down distance for nearby objects
+	//shrink dist by dist/16.
+	if (dist < 16.f)
+	{
+		dist /= 16.f;
+		dist *= dist;
+		dist *= 16.f;
+	}
+
+	//get area of circle around node
+	F32 app_angle = atanf(size.getLength3().getF32()/dist);
+	F32 radius = app_angle*LLDrawable::sCurPixelAngle;
+	return radius*radius * F_PI;
+}
+
 void LLPipeline::grabReferences(LLCullResult& result)
 {
 	sCull = &result;
 }
 
+void LLPipeline::clearReferences()
+{
+	sCull = NULL;
+}
+
+void check_references(LLSpatialGroup* group, LLDrawable* drawable)
+{
+	for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+	{
+		if (drawable == *i)
+		{
+			llerrs << "LLDrawable deleted while actively reference by LLPipeline." << llendl;
+		}
+	}			
+}
+
+void check_references(LLDrawable* drawable, LLFace* face)
+{
+	for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+	{
+		if (drawable->getFace(i) == face)
+		{
+			llerrs << "LLFace deleted while actively referenced by LLPipeline." << llendl;
+		}
+	}
+}
+
+void check_references(LLSpatialGroup* group, LLFace* face)
+{
+	for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+	{
+		LLDrawable* drawable = *i;
+		check_references(drawable, face);
+	}			
+}
+
+void LLPipeline::checkReferences(LLFace* face)
+{
+#if 0
+	if (sCull)
+	{
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, face);
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginAlphaGroups(); iter != sCull->endAlphaGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, face);
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, face);
+		}
+
+		for (LLCullResult::drawable_list_t::iterator iter = sCull->beginVisibleList(); iter != sCull->endVisibleList(); ++iter)
+		{
+			LLDrawable* drawable = *iter;
+			check_references(drawable, face);	
+		}
+	}
+#endif
+}
+
+void LLPipeline::checkReferences(LLDrawable* drawable)
+{
+#if 0
+	if (sCull)
+	{
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, drawable);
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginAlphaGroups(); iter != sCull->endAlphaGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, drawable);
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, drawable);
+		}
+
+		for (LLCullResult::drawable_list_t::iterator iter = sCull->beginVisibleList(); iter != sCull->endVisibleList(); ++iter)
+		{
+			if (drawable == *iter)
+			{
+				llerrs << "LLDrawable deleted while actively referenced by LLPipeline." << llendl;
+			}
+		}
+	}
+#endif
+}
+
+void check_references(LLSpatialGroup* group, LLDrawInfo* draw_info)
+{
+	for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+	{
+		LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
+		for (LLSpatialGroup::drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
+		{
+			LLDrawInfo* params = *j;
+			if (params == draw_info)
+			{
+				llerrs << "LLDrawInfo deleted while actively referenced by LLPipeline." << llendl;
+			}
+		}
+	}
+}
+
+
+void LLPipeline::checkReferences(LLDrawInfo* draw_info)
+{
+#if 0
+	if (sCull)
+	{
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, draw_info);
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginAlphaGroups(); iter != sCull->endAlphaGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, draw_info);
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			check_references(group, draw_info);
+		}
+	}
+#endif
+}
+
+void LLPipeline::checkReferences(LLSpatialGroup* group)
+{
+#if 0
+	if (sCull)
+	{
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter)
+		{
+			if (group == *iter)
+			{
+				llerrs << "LLSpatialGroup deleted while actively referenced by LLPipeline." << llendl;
+			}
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginAlphaGroups(); iter != sCull->endAlphaGroups(); ++iter)
+		{
+			if (group == *iter)
+			{
+				llerrs << "LLSpatialGroup deleted while actively referenced by LLPipeline." << llendl;
+			}
+		}
+
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter)
+		{
+			if (group == *iter)
+			{
+				llerrs << "LLSpatialGroup deleted while actively referenced by LLPipeline." << llendl;
+			}
+		}
+	}
+#endif
+}
+
+
 BOOL LLPipeline::visibleObjectsInFrustum(LLCamera& camera)
 {
 	for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
@@ -1563,7 +1934,7 @@ BOOL LLPipeline::getVisibleExtents(LLCamera& camera, LLVector3& min, LLVector3&
 
 static LLFastTimer::DeclareTimer FTM_CULL("Object Culling");
 
-void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip)
+void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip, LLPlane* planep)
 {
 	LLFastTimer t(FTM_CULL);
 	LLMemType mt_uc(LLMemType::MTYPE_PIPELINE_UPDATE_CULL);
@@ -1583,6 +1954,11 @@ void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_cl
 		mScreen.bindTarget();
 	}
 
+	if (sUseOcclusion > 1)
+	{
+		gGL.setColorMask(false, false);
+	}
+
 	glMatrixMode(GL_PROJECTION);
 	glPushMatrix();
 	glLoadMatrixd(gGLLastProjection);
@@ -1597,10 +1973,37 @@ void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_cl
 	LLGLDisable test(GL_ALPHA_TEST);
 	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 
-	if (sUseOcclusion > 1)
+
+	//setup a clip plane in projection matrix for reflection renders (prevents flickering from occlusion culling)
+	LLViewerRegion* region = gAgent.getRegion();
+	LLPlane plane;
+
+	if (planep)
 	{
-		gGL.setColorMask(false, false);
+		plane = *planep;
 	}
+	else 
+	{
+		if (region)
+		{
+			LLVector3 pnorm;
+			F32 height = region->getWaterHeight();
+			if (water_clip < 0)
+			{ //camera is above water, clip plane points up
+				pnorm.setVec(0,0,1);
+				plane.setVec(pnorm, -height);
+			}
+			else if (water_clip > 0)
+			{	//camera is below water, clip plane points down
+				pnorm = LLVector3(0,0,-1);
+				plane.setVec(pnorm, height);
+			}
+		}
+	}
+	
+	glh::matrix4f modelview = glh_get_last_modelview();
+	glh::matrix4f proj = glh_get_last_projection();
+	LLGLUserClipPlane clip(plane, modelview, proj, water_clip != 0 && LLPipeline::sReflectionRender);
 
 	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
 
@@ -1692,7 +2095,7 @@ void LLPipeline::markNotCulled(LLSpatialGroup* group, LLCamera& camera)
 	}
 
 	if (sMinRenderSize > 0.f && 
-			llmax(llmax(group->mBounds[1].mV[0], group->mBounds[1].mV[1]), group->mBounds[1].mV[2]) < sMinRenderSize)
+			llmax(llmax(group->mBounds[1][0], group->mBounds[1][1]), group->mBounds[1][2]) < sMinRenderSize)
 	{
 		return;
 	}
@@ -1736,33 +2139,34 @@ void LLPipeline::markOccluder(LLSpatialGroup* group)
 
 void LLPipeline::doOcclusion(LLCamera& camera)
 {
-	LLVertexBuffer::unbind();
-
-	if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
-	{
-		gGL.setColorMask(true, false, false, false);
-	}
-	else
+	if (LLPipeline::sUseOcclusion > 1 && sCull->hasOcclusionGroups())
 	{
-		gGL.setColorMask(false, false);
-	}
-	LLGLDisable blend(GL_BLEND);
-	LLGLDisable test(GL_ALPHA_TEST);
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+		LLVertexBuffer::unbind();
 
-	LLGLDisable cull(GL_CULL_FACE);
-	if (LLPipeline::sUseOcclusion > 1)
-	{
+		if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
+		{
+			gGL.setColorMask(true, false, false, false);
+		}
+		else
+		{
+			gGL.setColorMask(false, false);
+		}
+		LLGLDisable blend(GL_BLEND);
+		LLGLDisable test(GL_ALPHA_TEST);
+		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+		LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+
+		LLGLDisable cull(GL_CULL_FACE);
+		
 		for (LLCullResult::sg_list_t::iterator iter = sCull->beginOcclusionGroups(); iter != sCull->endOcclusionGroups(); ++iter)
 		{
 			LLSpatialGroup* group = *iter;
 			group->doOcclusion(&camera);
 			group->clearOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION);
 		}
+	
+		gGL.setColorMask(true, false);
 	}
-
-	gGL.setColorMask(true, false);
 }
 	
 BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority)
@@ -1789,17 +2193,14 @@ void LLPipeline::updateGL()
 
 void LLPipeline::rebuildPriorityGroups()
 {
-	if(!sAllowRebuildPriorityGroup)
-	{
-		return ;
-	}
-	sAllowRebuildPriorityGroup = FALSE ;
-
 	LLTimer update_timer;
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 	
 	assertInitialized();
 
+	gMeshRepo.notifyLoadedMeshes();
+
+	mGroupQ1Locked = true;
 	// Iterate through all drawables on the priority build queue,
 	for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ1.begin();
 		 iter != mGroupQ1.end(); ++iter)
@@ -1810,10 +2211,18 @@ void LLPipeline::rebuildPriorityGroups()
 	}
 
 	mGroupQ1.clear();
+	mGroupQ1Locked = false;
+
 }
 		
 void LLPipeline::rebuildGroups()
 {
+	if (mGroupQ2.empty())
+	{
+		return;
+	}
+
+	mGroupQ2Locked = true;
 	// Iterate through some drawables on the non-priority build queue
 	S32 size = (S32) mGroupQ2.size();
 	S32 min_count = llclamp((S32) ((F32) (size * size)/4096*0.25f), 1, size);
@@ -1823,33 +2232,30 @@ void LLPipeline::rebuildGroups()
 	std::sort(mGroupQ2.begin(), mGroupQ2.end(), LLSpatialGroup::CompareUpdateUrgency());
 
 	LLSpatialGroup::sg_vector_t::iterator iter;
+	LLSpatialGroup::sg_vector_t::iterator last_iter = mGroupQ2.begin();
+
 	for (iter = mGroupQ2.begin();
-		 iter != mGroupQ2.end(); ++iter)
+		 iter != mGroupQ2.end() && count <= min_count; ++iter)
 	{
 		LLSpatialGroup* group = *iter;
+		last_iter = iter;
 
-		if (group->isDead())
+		if (!group->isDead())
 		{
-			continue;
+			group->rebuildGeom();
+			
+			if (group->mSpatialPartition->mRenderByGroup)
+			{
+				count++;
+			}
 		}
 
-		group->rebuildGeom();
-		
-		if (group->mSpatialPartition->mRenderByGroup)
-		{
-			count++;
-		}
-			
 		group->clearState(LLSpatialGroup::IN_BUILD_Q2);
-
-		if (count > min_count)
-		{
-			++iter;
-			break;
-		}
 	}	
 
-	mGroupQ2.erase(mGroupQ2.begin(), iter);
+	mGroupQ2.erase(mGroupQ2.begin(), ++last_iter);
+
+	mGroupQ2Locked = false;
 
 	updateMovedList(mMovedBridge);
 }
@@ -2075,6 +2481,9 @@ void LLPipeline::shiftObjects(const LLVector3 &offset)
 	glClear(GL_DEPTH_BUFFER_BIT);
 	gDepthDirty = TRUE;
 		
+	LLVector4a offseta;
+	offseta.load3(offset.mV);
+
 	for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin();
 		 iter != mShiftList.end(); iter++)
 	{
@@ -2083,7 +2492,7 @@ void LLPipeline::shiftObjects(const LLVector3 &offset)
 		{
 			continue;
 		}	
-		drawablep->shiftPos(offset);	
+		drawablep->shiftPos(offseta);	
 		drawablep->clearState(LLDrawable::ON_SHIFT_LIST);
 	}
 	mShiftList.resize(0);
@@ -2097,7 +2506,7 @@ void LLPipeline::shiftObjects(const LLVector3 &offset)
 			LLSpatialPartition* part = region->getSpatialPartition(i);
 			if (part)
 			{
-				part->shift(offset);
+				part->shift(offseta);
 			}
 		}
 	}
@@ -2129,8 +2538,7 @@ void LLPipeline::markGLRebuild(LLGLUpdate* glu)
 void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	//assert_main_thread();
-
+	
 	if (group && !group->isDead() && group->mSpatialPartition)
 	{
 		if (group->mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_HUD)
@@ -2142,6 +2550,8 @@ void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority)
 		{
 			if (!group->isState(LLSpatialGroup::IN_BUILD_Q1))
 			{
+				llassert_always(!mGroupQ1Locked);
+
 				mGroupQ1.push_back(group);
 				group->setState(LLSpatialGroup::IN_BUILD_Q1);
 
@@ -2158,11 +2568,7 @@ void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority)
 		}
 		else if (!group->isState(LLSpatialGroup::IN_BUILD_Q2 | LLSpatialGroup::IN_BUILD_Q1))
 		{
-			//llerrs << "Non-priority updates not yet supported!" << llendl;
-			if (std::find(mGroupQ2.begin(), mGroupQ2.end(), group) != mGroupQ2.end())
-			{
-				llerrs << "WTF?" << llendl;
-			}
+			llassert_always(!mGroupQ2Locked);
 			mGroupQ2.push_back(group);
 			group->setState(LLSpatialGroup::IN_BUILD_Q2);
 
@@ -2242,6 +2648,42 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result)
 			}
 		}
 	}
+
+	if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD)
+	{
+		LLSpatialGroup* last_group = NULL;
+		for (LLCullResult::bridge_list_t::iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i)
+		{
+			LLCullResult::bridge_list_t::iterator cur_iter = i;
+			LLSpatialBridge* bridge = *cur_iter;
+			LLSpatialGroup* group = bridge->getSpatialGroup();
+
+			if (last_group == NULL)
+			{
+				last_group = group;
+			}
+
+			if (!bridge->isDead() && group && !group->isOcclusionState(LLSpatialGroup::OCCLUDED))
+			{
+				stateSort(bridge, camera);
+			}
+
+			if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD &&
+				last_group != group && last_group->changeLOD())
+			{
+				last_group->mLastUpdateDistance = last_group->mDistance;
+			}
+
+			last_group = group;
+		}
+
+		if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD &&
+			last_group && last_group->changeLOD())
+		{
+			last_group->mLastUpdateDistance = last_group->mDistance;
+		}
+	}
+
 	for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter)
 	{
 		LLSpatialGroup* group = *iter;
@@ -2257,19 +2699,6 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result)
 		}
 	}
 	
-	if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD)
-	{
-		for (LLCullResult::bridge_list_t::iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i)
-		{
-			LLCullResult::bridge_list_t::iterator cur_iter = i;
-			LLSpatialBridge* bridge = *cur_iter;
-			LLSpatialGroup* group = bridge->getSpatialGroup();
-			if (!bridge->isDead() && group && !group->isOcclusionState(LLSpatialGroup::OCCLUDED))
-			{
-				stateSort(bridge, camera);
-			}
-		}
-	}
 	{
 		LLFastTimer ftm(FTM_STATESORT_DRAWABLE);
 		for (LLCullResult::drawable_list_t::iterator iter = sCull->beginVisibleList();
@@ -2300,6 +2729,11 @@ void LLPipeline::stateSort(LLSpatialGroup* group, LLCamera& camera)
 			LLDrawable* drawablep = *i;
 			stateSort(drawablep, camera);
 		}
+
+		if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD)
+		{ //avoid redundant stateSort calls
+			group->mLastUpdateDistance = group->mDistance;
+		}
 	}
 
 }
@@ -2307,7 +2741,7 @@ void LLPipeline::stateSort(LLSpatialGroup* group, LLCamera& camera)
 void LLPipeline::stateSort(LLSpatialBridge* bridge, LLCamera& camera)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT);
-	if (!sShadowRender && bridge->getSpatialGroup()->changeLOD())
+	if (bridge->getSpatialGroup()->changeLOD())
 	{
 		bool force_update = false;
 		bridge->updateDistance(camera, force_update);
@@ -2366,21 +2800,17 @@ void LLPipeline::stateSort(LLDrawable* drawablep, LLCamera& camera)
 
 	if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD)
 	{
-		LLSpatialGroup* group = drawablep->getSpatialGroup();
-		if (!group || group->changeLOD())
+		//if (drawablep->isVisible()) isVisible() check here is redundant, if it wasn't visible, it wouldn't be here
 		{
-			if (drawablep->isVisible())
+			if (!drawablep->isActive())
 			{
-				if (!drawablep->isActive())
-				{
-					bool force_update = false;
-					drawablep->updateDistance(camera, force_update);
-				}
-				else if (drawablep->isAvatar())
-				{
-					bool force_update = false;
-					drawablep->updateDistance(camera, force_update); // calls vobj->updateLOD() which calls LLVOAvatar::updateVisibility()
-				}
+				bool force_update = false;
+				drawablep->updateDistance(camera, force_update);
+			}
+			else if (drawablep->isAvatar())
+			{
+				bool force_update = false;
+				drawablep->updateDistance(camera, force_update); // calls vobj->updateLOD() which calls LLVOAvatar::updateVisibility()
 			}
 		}
 	}
@@ -2608,21 +3038,6 @@ void LLPipeline::postSort(LLCamera& camera)
 	//rebuild groups
 	sCull->assertDrawMapsEmpty();
 
-	/*LLSpatialGroup::sNoDelete = FALSE;
-	for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i)
-	{
-		LLSpatialGroup* group = *i;
-		if (sUseOcclusion && 
-			group->isState(LLSpatialGroup::OCCLUDED))
-		{
-			continue;
-		}
-		
-		group->rebuildGeom();
-	}
-	LLSpatialGroup::sNoDelete = TRUE;*/
-
-
 	rebuildPriorityGroups();
 	llpushcallstacks ;
 
@@ -2668,8 +3083,10 @@ void LLPipeline::postSort(LLCamera& camera)
 			{
 				if (sMinRenderSize > 0.f)
 				{
-					LLVector3 bounds = (*k)->mExtents[1]-(*k)->mExtents[0];
-					if (llmax(llmax(bounds.mV[0], bounds.mV[1]), bounds.mV[2]) > sMinRenderSize)
+					LLVector4a bounds;
+					bounds.setSub((*k)->mExtents[1],(*k)->mExtents[0]);
+
+					if (llmax(llmax(bounds[0], bounds[1]), bounds[2]) > sMinRenderSize)
 					{
 						sCull->pushDrawInfo(j->first, *k);
 					}
@@ -2830,7 +3247,7 @@ void render_hud_elements()
 	gGL.color4f(1,1,1,1);
 	if (!LLPipeline::sReflectionRender && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
 	{
-		LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+		LLGLEnable multisample(gSavedSettings.getU32("RenderFSAASamples") > 0 ? GL_MULTISAMPLE_ARB : 0);
 		gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD version in render_ui_3d()
 	
 		// Draw the tracking overlays
@@ -3044,6 +3461,13 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
 		}
 	}
 
+	S32 stack_depth = 0;
+
+	if (gDebugGL)
+	{
+		glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &stack_depth);
+	}
+
 	///////////////////////////////////////////
 	//
 	// Sync and verify GL state
@@ -3074,7 +3498,7 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
 	glMatrixMode(GL_MODELVIEW);
 
 	LLGLSPipeline gls_pipeline;
-	LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+	LLGLEnable multisample(gSavedSettings.getU32("RenderFSAASamples") > 0 ? GL_MULTISAMPLE_ARB : 0);
 
 	LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2);
 				
@@ -3168,18 +3592,9 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
 					}
 					poolp->endRenderPass(i);
 					LLVertexBuffer::unbind();
-					if (gDebugGL || gDebugPipeline)
+					if (gDebugGL)
 					{
-						GLint depth;
-						glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth);
-						if (depth > 3)
-						{
-							if (gDebugSession)
-							{
-								ll_fail("GL matrix stack corrupted.");
-							}
-							llerrs << "GL matrix stack corrupted!" << llendl;
-						}
+						check_stack_depth(stack_depth);
 						std::string msg = llformat("%s pass %d", gPoolNames[cur_type].c_str(), i);
 						LLGLState::checkStates(msg);
 						LLGLState::checkTextureChannels(msg);
@@ -3200,13 +3615,13 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
 				}
 			}
 			iter1 = iter2;
-			stop_glerror();
-		}
-	
-	LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPoolsEnd");
-
-	LLVertexBuffer::unbind();
+			stop_glerror();
+		}
 		
+		LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPoolsEnd");
+
+		LLVertexBuffer::unbind();
+			
 		gGLLastMatrix = NULL;
 		glLoadMatrixd(gGLModelView);
 
@@ -3253,9 +3668,9 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
 	{
 		if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
 		{
-		      // Render debugging beacons.
-		      gObjectList.renderObjectBeacons();
-		      gObjectList.resetObjectBeacons();
+			// Render debugging beacons.
+			gObjectList.renderObjectBeacons();
+			gObjectList.resetObjectBeacons();
 		}
 		else
 		{
@@ -3314,7 +3729,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera)
 		}
 	}
 
-	LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+	LLGLEnable multisample(gSavedSettings.getU32("RenderFSAASamples") > 0 ? GL_MULTISAMPLE_ARB : 0);
 
 	LLVertexBuffer::unbind();
 
@@ -3403,7 +3818,7 @@ void LLPipeline::renderGeomPostDeferred(LLCamera& camera)
 
 	LLGLEnable cull(GL_CULL_FACE);
 
-	LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+	LLGLEnable multisample(gSavedSettings.getU32("RenderFSAASamples") > 0 ? GL_MULTISAMPLE_ARB : 0);
 
 	calcNearbyLights(camera);
 	setupHWLights(NULL);
@@ -3588,6 +4003,59 @@ void LLPipeline::addTrianglesDrawn(S32 index_count, U32 render_type)
 	}
 }
 
+void LLPipeline::renderPhysicsDisplay()
+{
+	if (!hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PHYSICS_SHAPES))
+	{
+		return;
+	}
+
+	allocatePhysicsBuffer();
+
+	gGL.flush();
+	mPhysicsDisplay.bindTarget();
+	glClearColor(0,0,0,1);
+	gGL.setColorMask(true, true);
+	mPhysicsDisplay.clear();
+	glClearColor(0,0,0,0);
+
+	gGL.setColorMask(true, false);
+
+	for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
+			iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
+	{
+		LLViewerRegion* region = *iter;
+		for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++)
+		{
+			LLSpatialPartition* part = region->getSpatialPartition(i);
+			if (part)
+			{
+				if (hasRenderType(part->mDrawableType))
+				{
+					part->renderPhysicsShapes();
+				}
+			}
+		}
+	}
+
+	for (LLCullResult::bridge_list_t::const_iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i)
+	{
+		LLSpatialBridge* bridge = *i;
+		if (!bridge->isDead() && hasRenderType(bridge->mDrawableType))
+		{
+			glPushMatrix();
+			glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix);
+			bridge->renderPhysicsShapes();
+			glPopMatrix();
+		}
+	}
+
+
+	gGL.flush();
+	mPhysicsDisplay.flush();
+}
+
+
 void LLPipeline::renderDebug()
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
@@ -3600,6 +4068,8 @@ void LLPipeline::renderDebug()
 	glLoadMatrixd(gGLModelView);
 	gGL.setColorMask(true, false);
 
+	bool hud_only = hasRenderType(LLPipeline::RENDER_TYPE_HUD);
+
 	// Debug stuff.
 	for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
 			iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
@@ -3610,7 +4080,8 @@ void LLPipeline::renderDebug()
 			LLSpatialPartition* part = region->getSpatialPartition(i);
 			if (part)
 			{
-				if (hasRenderType(part->mDrawableType))
+				if ( hud_only && (part->mDrawableType == RENDER_TYPE_HUD || part->mDrawableType == RENDER_TYPE_HUD_PARTICLES) ||
+					 !hud_only && hasRenderType(part->mDrawableType) )
 				{
 					part->renderDebug();
 				}
@@ -3630,8 +4101,67 @@ void LLPipeline::renderDebug()
 		}
 	}
 
+	if (gSavedSettings.getBOOL("DebugShowUploadCost"))
+	{
+		std::set<LLUUID> textures;
+		std::set<LLUUID> sculpts;
+		std::set<LLUUID> meshes;
+		
+		BOOL selected = TRUE;
+		if (LLSelectMgr::getInstance()->getSelection()->isEmpty())
+		{
+			selected = FALSE;
+		}
+			
+		for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
+			for (LLSpatialGroup::OctreeNode::element_iter elem = node->getData().begin(); elem != node->getData().end(); ++elem)
+			{
+				LLDrawable* drawable = *elem;
+				LLVOVolume* volume = drawable->getVOVolume();
+				if (volume && volume->isSelected() == selected)
+				{
+					for (U32 i = 0; i < volume->getNumTEs(); ++i)
+					{
+						LLTextureEntry* te = volume->getTE(i);
+						textures.insert(te->getID());
+					}
+
+					if (volume->isSculpted())
+					{
+						LLUUID sculpt_id = volume->getVolume()->getParams().getSculptID();
+						if (volume->isMesh())
+						{
+							meshes.insert(sculpt_id);
+						}
+						else
+						{
+							sculpts.insert(sculpt_id);
+						}
+					}
+				}
+			}
+		}
+
+		gPipeline.mDebugTextureUploadCost = textures.size() * 10;
+		gPipeline.mDebugSculptUploadCost = sculpts.size()*10;
+		
+		U32 mesh_cost = 0;
+
+		for (std::set<LLUUID>::iterator iter = meshes.begin(); iter != meshes.end(); ++iter)
+		{
+			mesh_cost += gMeshRepo.getResourceCost(*iter)*10;
+		}
+
+		gPipeline.mDebugMeshUploadCost = mesh_cost;
+	}
+
 	if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA))
 	{
+		LLVertexBuffer::unbind();
+
 		LLGLEnable blend(GL_BLEND);
 		LLGLDepthTest depth(TRUE, FALSE);
 		LLGLDisable cull(GL_CULL_FACE);
@@ -3695,7 +4225,7 @@ void LLPipeline::renderDebug()
 			if (i < 4)
 			{
 				
-				if (i == 0 || !mShadowFrustPoints[i].empty())
+				//if (i == 0 || !mShadowFrustPoints[i].empty())
 				{
 					//render visible point cloud
 					gGL.flush();
@@ -3735,11 +4265,12 @@ void LLPipeline::renderDebug()
 					gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[6].mV);
 					gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[7].mV);
 					gGL.end();
-					}
-
+				}
 			}
 
-			/*for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
+			/*gGL.flush();
+			glLineWidth(16-i*2);
+			for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
 					iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
 			{
 				LLViewerRegion* region = *iter;
@@ -3754,7 +4285,9 @@ void LLPipeline::renderDebug()
 						}
 					}
 				}
-			}*/
+			}
+			gGL.flush();
+			glLineWidth(1.f);*/
 		}
 	}
 
@@ -3794,13 +4327,19 @@ void LLPipeline::renderDebug()
 	if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_BUILD_QUEUE)
 	{
 		U32 count = 0;
-		U32 size = mBuildQ2.size();
+		U32 size = mGroupQ2.size();
 		LLColor4 col;
 
+		LLVertexBuffer::unbind();
 		LLGLEnable blend(GL_BLEND);
+		gGL.setSceneBlendType(LLRender::BT_ALPHA);
 		LLGLDepthTest depth(GL_TRUE, GL_FALSE);
 		gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
 		
+		gGL.pushMatrix();
+		glLoadMatrixd(gGLModelView);
+		gGLLastMatrix = NULL;
+
 		for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ2.begin(); iter != mGroupQ2.end(); ++iter)
 		{
 			LLSpatialGroup* group = *iter;
@@ -3822,7 +4361,7 @@ void LLPipeline::renderDebug()
 				glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix);
 			}
 
-			F32 alpha = (F32) (size-count)/size;
+			F32 alpha = llclamp((F32) (size-count)/size, 0.f, 1.f);
 
 			
 			LLVector2 c(1.f-alpha, alpha);
@@ -3830,7 +4369,7 @@ void LLPipeline::renderDebug()
 
 			
 			++count;
-			col.set(c.mV[0], c.mV[1], 0, alpha*0.5f+0.1f);
+			col.set(c.mV[0], c.mV[1], 0, alpha*0.5f+0.5f);
 			group->drawObjectBox(col);
 
 			if (bridge)
@@ -3838,9 +4377,13 @@ void LLPipeline::renderDebug()
 				gGL.popMatrix();
 			}
 		}
+
+		gGL.popMatrix();
 	}
 
 	gGL.flush();
+
+	gPipeline.renderPhysicsDisplay();
 }
 
 void LLPipeline::rebuildPools()
@@ -4172,16 +4715,19 @@ void LLPipeline::setupAvatarLights(BOOL for_edit)
 		
 		light_pos.normalize();
 
+		LLLightState* light = gGL.getLight(1);
+
 		mHWLightColors[1] = diffuse;
-		glLightfv(GL_LIGHT1, GL_DIFFUSE,  diffuse.mV);
-		glLightfv(GL_LIGHT1, GL_AMBIENT,  LLColor4::black.mV);
-		glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV);
-		glLightfv(GL_LIGHT1, GL_POSITION, light_pos.mV); 
-		glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION,  1.0f);
-		glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 	 0.0f);
-		glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f);
-		glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 		 0.0f);
-		glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 			 180.0f);
+
+		light->setDiffuse(diffuse);
+		light->setAmbient(LLColor4::black);
+		light->setSpecular(LLColor4::black);
+		light->setPosition(light_pos);
+		light->setConstantAttenuation(1.f);
+		light->setLinearAttenuation(0.f);
+		light->setQuadraticAttenuation(0.f);
+		light->setSpotExponent(0.f);
+		light->setSpotCutoff(180.f);
 	}
 	else if (gAvatarBacklight) // Always true (unless overridden in a devs .ini)
 	{
@@ -4212,22 +4758,28 @@ void LLPipeline::setupAvatarLights(BOOL for_edit)
 		backlight_diffuse *= backlight_mag / max_component;
 
 		mHWLightColors[1] = backlight_diffuse;
-		glLightfv(GL_LIGHT1, GL_POSITION, backlight_pos.mV); // this is just sun/moon direction
-		glLightfv(GL_LIGHT1, GL_DIFFUSE,  backlight_diffuse.mV);
-		glLightfv(GL_LIGHT1, GL_AMBIENT,  LLColor4::black.mV);
-		glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV);
-		glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION,  1.0f);
-		glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION,    0.0f);
-		glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f);
-		glLightf (GL_LIGHT1, GL_SPOT_EXPONENT,         0.0f);
-		glLightf (GL_LIGHT1, GL_SPOT_CUTOFF,           180.0f);
+
+		LLLightState* light = gGL.getLight(1);
+
+		light->setPosition(backlight_pos);
+		light->setDiffuse(backlight_diffuse);
+		light->setAmbient(LLColor4::black);
+		light->setSpecular(LLColor4::black);
+		light->setConstantAttenuation(1.f);
+		light->setLinearAttenuation(0.f);
+		light->setQuadraticAttenuation(0.f);
+		light->setSpotExponent(0.f);
+		light->setSpotCutoff(180.f);
 	}
 	else
 	{
+		LLLightState* light = gGL.getLight(1);
+
 		mHWLightColors[1] = LLColor4::black;
-		glLightfv(GL_LIGHT1, GL_DIFFUSE,  LLColor4::black.mV);
-		glLightfv(GL_LIGHT1, GL_AMBIENT,  LLColor4::black.mV);
-		glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV);
+
+		light->setDiffuse(LLColor4::black);
+		light->setAmbient(LLColor4::black);
+		light->setSpecular(LLColor4::black);
 	}
 }
 
@@ -4246,7 +4798,7 @@ static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_
 	{
 		return max_dist;
 	}
-	F32 dist = fsqrtf(dist2);
+	F32 dist = (F32) sqrt(dist2);
 	dist *= 1.f / inten;
 	dist -= radius;
 	if (selected)
@@ -4275,7 +4827,7 @@ void LLPipeline::calcNearbyLights(LLCamera& camera)
 		// mNearbyLight (and all light_set_t's) are sorted such that
 		// begin() == the closest light and rbegin() == the farthest light
 		const S32 MAX_LOCAL_LIGHTS = 6;
-// 		LLVector3 cam_pos = gAgentCamera.getCameraPositionAgent();
+// 		LLVector3 cam_pos = gAgent.getCameraPositionAgent();
 		LLVector3 cam_pos = LLViewerJoystick::getInstance()->getOverrideCamera() ?
 						camera.getOrigin() : 
 						gAgent.getPositionAgent();
@@ -4408,15 +4960,17 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 		LLVector4 light_pos(mSunDir, 0.0f);
 		LLColor4 light_diffuse = mSunDiffuse;
 		mHWLightColors[0] = light_diffuse;
-		glLightfv(GL_LIGHT0, GL_POSITION, light_pos.mV); // this is just sun/moon direction
-		glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse.mV);
-		glLightfv(GL_LIGHT0, GL_AMBIENT,  LLColor4::black.mV);
-		glLightfv(GL_LIGHT0, GL_SPECULAR, LLColor4::black.mV);
-		glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION,  1.0f);
-		glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION,    0.0f);
-		glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f);
-		glLightf (GL_LIGHT0, GL_SPOT_EXPONENT,         0.0f);
-		glLightf (GL_LIGHT0, GL_SPOT_CUTOFF,           180.0f);
+
+		LLLightState* light = gGL.getLight(0);
+		light->setPosition(light_pos);
+		light->setDiffuse(light_diffuse);
+		light->setAmbient(LLColor4::black);
+		light->setSpecular(LLColor4::black);
+		light->setConstantAttenuation(1.f);
+		light->setLinearAttenuation(0.f);
+		light->setQuadraticAttenuation(0.f);
+		light->setSpotExponent(0.f);
+		light->setSpotCutoff(180.f);
 	}
 	
 	// Light 1 = Backlight (for avatars)
@@ -4474,13 +5028,23 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 			float linatten = x / (light_radius); // % of brightness at radius
 
 			mHWLightColors[cur_light] = light_color;
-			S32 gllight = GL_LIGHT0+cur_light;
-			glLightfv(gllight, GL_POSITION, light_pos_gl.mV);
-			glLightfv(gllight, GL_DIFFUSE,  light_color.mV);
-			glLightfv(gllight, GL_AMBIENT,  LLColor4::black.mV);
-			glLightf (gllight, GL_CONSTANT_ATTENUATION,   0.0f);
-			glLightf (gllight, GL_LINEAR_ATTENUATION,     linatten);
-			glLightf (gllight, GL_QUADRATIC_ATTENUATION,  0.0f);
+			LLLightState* light_state = gGL.getLight(cur_light);
+			
+			light_state->setPosition(light_pos_gl);
+			light_state->setDiffuse(light_color);
+			light_state->setAmbient(LLColor4::black);
+			light_state->setConstantAttenuation(0.f);
+			if (sRenderDeferred)
+			{
+				light_state->setLinearAttenuation(light_radius*1.5f);
+				light_state->setQuadraticAttenuation(light->getLightFalloff()*0.5f+1.f);
+			}
+			else
+			{
+				light_state->setLinearAttenuation(linatten);
+				light_state->setQuadraticAttenuation(0.f);
+			}
+			
 			if (light->isLightSpotlight() // directional (spot-)light
 			    && (LLPipeline::sRenderDeferred || gSavedSettings.getBOOL("RenderSpotLightsInNondeferred"))) // these are only rendered as GL spotlights if we're in deferred rendering mode *or* the setting forces them on
 			{
@@ -4488,22 +5052,21 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 				LLQuaternion quat = light->getRenderRotation();
 				LLVector3 at_axis(0,0,-1); // this matches deferred rendering's object light direction
 				at_axis *= quat;
-				//llinfos << "SPOT!!!!!!! fov: " << spotparams.mV[0] << " focus: " << spotparams.mV[1] << " dir: " << at_axis << llendl;
-				glLightfv(gllight, GL_SPOT_DIRECTION, at_axis.mV);
-				glLightf (gllight, GL_SPOT_EXPONENT,  2.0f); // 2.0 = good old dot product ^ 2
-				glLightf (gllight, GL_SPOT_CUTOFF,    90.0f); // hemisphere
-				const float specular[] = {0.f, 0.f, 0.f, 0.f};
-				glLightfv(gllight, GL_SPECULAR, specular);
+
+				light_state->setSpotDirection(at_axis);
+				light_state->setSpotCutoff(90.f);
+				light_state->setSpotExponent(2.f);
+	
+				light_state->setSpecular(LLColor4::black);
 			}
 			else // omnidirectional (point) light
 			{
-			glLightf (gllight, GL_SPOT_EXPONENT,          0.0f);
-			glLightf (gllight, GL_SPOT_CUTOFF,            180.0f);
-
+				light_state->setSpotExponent(0.f);
+				light_state->setSpotCutoff(180.f);
+				
 				// we use specular.w = 1.0 as a cheap hack for the shaders to know that this is omnidirectional rather than a spotlight
-				const float specular[] = {0.f, 0.f, 0.f, 1.f};
-				glLightfv(gllight, GL_SPECULAR, specular);
-				//llinfos << "boring light" << llendl;
+				const LLColor4 specular(0.f, 0.f, 0.f, 1.f);
+				light_state->setSpecular(specular);				
 			}
 			cur_light++;
 			if (cur_light >= 8)
@@ -4515,13 +5078,13 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 	for ( ; cur_light < 8 ; cur_light++)
 	{
 		mHWLightColors[cur_light] = LLColor4::black;
-		S32 gllight = GL_LIGHT0+cur_light;
-		glLightfv(gllight, GL_DIFFUSE,  LLColor4::black.mV);
-		glLightfv(gllight, GL_AMBIENT,  LLColor4::black.mV);
-		glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV);
-	}
+		LLLightState* light = gGL.getLight(cur_light);
 
-	if (isAgentAvatarValid() &&
+		light->setDiffuse(LLColor4::black);
+		light->setAmbient(LLColor4::black);
+		light->setSpecular(LLColor4::black);
+	}
+	if (gAgentAvatarp &&
 		gAgentAvatarp->mSpecialRenderMode == 3)
 	{
 		LLColor4  light_color = LLColor4::white;
@@ -4536,23 +5099,24 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 		float linatten = x / (light_radius); // % of brightness at radius
 
 		mHWLightColors[2] = light_color;
-		S32 gllight = GL_LIGHT2;
-		glLightfv(gllight, GL_POSITION, light_pos_gl.mV);
-		glLightfv(gllight, GL_DIFFUSE,  light_color.mV);
-		glLightfv(gllight, GL_AMBIENT,  LLColor4::black.mV);
-		glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV);
-		glLightf (gllight, GL_CONSTANT_ATTENUATION,   0.0f);
-		glLightf (gllight, GL_LINEAR_ATTENUATION,     linatten);
-		glLightf (gllight, GL_QUADRATIC_ATTENUATION,  0.0f);
-		glLightf (gllight, GL_SPOT_EXPONENT,          0.0f);
-		glLightf (gllight, GL_SPOT_CUTOFF,            180.0f);
+		LLLightState* light = gGL.getLight(2);
+
+		light->setPosition(light_pos_gl);
+		light->setDiffuse(light_color);
+		light->setAmbient(LLColor4::black);
+		light->setSpecular(LLColor4::black);
+		light->setQuadraticAttenuation(0.f);
+		light->setConstantAttenuation(0.f);
+		light->setLinearAttenuation(linatten);
+		light->setSpotExponent(0.f);
+		light->setSpotCutoff(180.f);
 	}
 
 	// Init GL state
 	glDisable(GL_LIGHTING);
-	for (S32 gllight=GL_LIGHT0; gllight<=GL_LIGHT7; gllight++)
+	for (S32 i = 0; i < 8; ++i)
 	{
-		glDisable(gllight);
+		gGL.getLight(i)->disable();
 	}
 	mLightMask = 0;
 }
@@ -4577,15 +5141,16 @@ void LLPipeline::enableLights(U32 mask)
 			stop_glerror();
 			for (S32 i=0; i<8; i++)
 			{
+				LLLightState* light = gGL.getLight(i);
 				if (mask & (1<<i))
 				{
-					glEnable(GL_LIGHT0 + i);
-					glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  mHWLightColors[i].mV);
+					light->enable();
+					light->setDiffuse(mHWLightColors[i]);
 				}
 				else
 				{
-					glDisable(GL_LIGHT0 + i);
-					glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  LLColor4::black.mV);
+					light->disable();
+					light->setDiffuse(LLColor4::black);
 				}
 			}
 			stop_glerror();
@@ -4609,7 +5174,6 @@ void LLPipeline::enableLightsStatic()
 	if (mLightingDetail >= 2)
 	{
 		mask |= mLightMovingMask; // Hardware moving lights
-		glColor4f(0.f, 0.f, 0.f, 1.0f); // no local lighting by default
 	}
 	else
 	{
@@ -4623,11 +5187,7 @@ void LLPipeline::enableLightsDynamic()
 	assertInitialized();
 	U32 mask = 0xff & (~2); // Local lights
 	enableLights(mask);
-	if (mLightingDetail >= 2)
-	{
-		glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default
-	}
-
+	
 	if (isAgentAvatarValid() && getLightingDetail() <= 0)
 	{
 		if (gAgentAvatarp->mSpecialRenderMode == 0) // normal
@@ -4648,6 +5208,65 @@ void LLPipeline::enableLightsAvatar()
 	enableLights(mask);
 }
 
+void LLPipeline::enableLightsPreview()
+{
+	disableLights();
+
+	glEnable(GL_LIGHTING);
+	LLColor4 ambient = gSavedSettings.getColor4("PreviewAmbientColor");
+	glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV);
+
+
+	LLColor4 diffuse0 = gSavedSettings.getColor4("PreviewDiffuse0");
+	LLColor4 specular0 = gSavedSettings.getColor4("PreviewSpecular0");
+	LLColor4 diffuse1 = gSavedSettings.getColor4("PreviewDiffuse1");
+	LLColor4 specular1 = gSavedSettings.getColor4("PreviewSpecular1");
+	LLColor4 diffuse2 = gSavedSettings.getColor4("PreviewDiffuse2");
+	LLColor4 specular2 = gSavedSettings.getColor4("PreviewSpecular2");
+
+	LLVector3 dir0 = gSavedSettings.getVector3("PreviewDirection0");
+	LLVector3 dir1 = gSavedSettings.getVector3("PreviewDirection1");
+	LLVector3 dir2 = gSavedSettings.getVector3("PreviewDirection2");
+
+	dir0.normVec();
+	dir1.normVec();
+	dir2.normVec();
+	
+	LLVector4 light_pos(dir0, 0.0f);
+
+	LLLightState* light = gGL.getLight(0);
+
+	light->enable();
+	light->setPosition(light_pos);
+	light->setDiffuse(diffuse0);
+	light->setAmbient(LLColor4::black);
+	light->setSpecular(specular0);
+	light->setSpotExponent(0.f);
+	light->setSpotCutoff(180.f);
+
+	light_pos = LLVector4(dir1, 0.f);
+
+	light = gGL.getLight(1);
+	light->enable();
+	light->setPosition(light_pos);
+	light->setDiffuse(diffuse1);
+	light->setAmbient(LLColor4::black);
+	light->setSpecular(specular1);
+	light->setSpotExponent(0.f);
+	light->setSpotCutoff(180.f);
+
+	light_pos = LLVector4(dir2, 0.f);
+	light = gGL.getLight(2);
+	light->enable();
+	light->setPosition(light_pos);
+	light->setDiffuse(diffuse2);
+	light->setAmbient(LLColor4::black);
+	light->setSpecular(specular2);
+	light->setSpotExponent(0.f);
+	light->setSpotCutoff(180.f);
+}
+
+
 void LLPipeline::enableLightsAvatarEdit(const LLColor4& color)
 {
 	U32 mask = 0x2002; // Avatar backlight only, set ambient
@@ -4664,16 +5283,11 @@ void LLPipeline::enableLightsFullbright(const LLColor4& color)
 	enableLights(mask);
 
 	glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV);
-	/*if (mLightingDetail >= 2)
-	{
-		glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default
-	}*/
 }
 
 void LLPipeline::disableLights()
 {
 	enableLights(0); // no lighting (full bright)
-	glColor4f(1.f, 1.f, 1.f, 1.f); // lighting color = white by default
 }
 
 //============================================================================
@@ -4939,6 +5553,18 @@ BOOL LLPipeline::toggleRenderDebugFeatureControl(void* data)
 	return gPipeline.hasRenderDebugFeatureMask(bit);
 }
 
+void LLPipeline::setRenderDebugFeatureControl(U32 bit, bool value)
+{
+	if (value)
+	{
+		gPipeline.mRenderDebugFeatureMask |= bit;
+	}
+	else
+	{
+		gPipeline.mRenderDebugFeatureMask &= !bit;
+	}
+}
+
 // static
 void LLPipeline::setRenderScriptedBeacons(BOOL val)
 {
@@ -5289,17 +5915,12 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable)
 	for (S32 i = 0; i < drawable->getNumFaces(); i++)
 	{
 		LLFace* facep = drawable->getFace(i);
-		facep->mVertexBuffer = NULL;
-		facep->mLastVertexBuffer = NULL;
+		facep->clearVertexBuffer();
 	}
 }
 
 void LLPipeline::resetVertexBuffers()
-{
-	sRenderBump = gSavedSettings.getBOOL("RenderObjectBump");
-	sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips");
-	LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO");
-
+{	
 	for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
 			iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
 	{
@@ -5339,8 +5960,16 @@ void LLPipeline::resetVertexBuffers()
 		llwarns << "VBO name pool cleanup failed." << llendl;
 	}
 
-	LLVertexBuffer::unbind();
-
+	LLVertexBuffer::unbind();	
+	
+	sRenderBump = gSavedSettings.getBOOL("RenderObjectBump");
+	sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips");
+	LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO");
+	LLVertexBuffer::sPreferStreamDraw = gSavedSettings.getBOOL("RenderPreferStreamDraw");
+	LLVertexBuffer::sEnableVBOs = gSavedSettings.getBOOL("RenderVBOEnable");
+	LLVertexBuffer::sDisableVBOMapping = LLVertexBuffer::sEnableVBOs && gSavedSettings.getBOOL("RenderVBOMappingDisable") ;
+	sBakeSunlight = gSavedSettings.getBOOL("RenderBakeSunlight");
+	sNoAlpha = gSavedSettings.getBOOL("RenderNoAlpha");
 	LLPipeline::sTextureBindTest = gSavedSettings.getBOOL("RenderDebugTextureBind");
 }
 
@@ -5355,42 +5984,6 @@ void LLPipeline::renderObjects(U32 type, U32 mask, BOOL texture)
 	gGLLastMatrix = NULL;		
 }
 
-void LLPipeline::setUseVBO(BOOL use_vbo)
-{
-	if (use_vbo != LLVertexBuffer::sEnableVBOs)
-	{
-		if (use_vbo)
-		{
-			llinfos << "Enabling VBO." << llendl;
-		}
-		else
-		{ 
-			llinfos << "Disabling VBO." << llendl;
-		}
-		
-		resetVertexBuffers();
-		LLVertexBuffer::initClass(use_vbo, gSavedSettings.getBOOL("RenderVBOMappingDisable"));
-	}
-}
-
-void LLPipeline::setDisableVBOMapping(BOOL no_vbo_mapping)
-{
-	if (LLVertexBuffer::sEnableVBOs && no_vbo_mapping != LLVertexBuffer::sDisableVBOMapping)
-	{
-		if (no_vbo_mapping)
-		{
-			llinfos << "Disabling VBO glMapBufferARB." << llendl;
-		}
-		else
-		{ 
-			llinfos << "Enabling VBO glMapBufferARB." << llendl;
-		}
-		
-		resetVertexBuffers();
-		LLVertexBuffer::initClass(true, no_vbo_mapping);
-	}
-}
-
 void apply_cube_face_rotation(U32 face)
 {
 	switch (face)
@@ -5422,21 +6015,21 @@ void apply_cube_face_rotation(U32 face)
 void validate_framebuffer_object()
 {                                                           
 	GLenum status;                                            
-	status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 
+	status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); 
 	switch(status) 
 	{                                          
-		case GL_FRAMEBUFFER_COMPLETE_EXT:                       
+		case GL_FRAMEBUFFER_COMPLETE:                       
 			//framebuffer OK, no error.
 			break;
-		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
 			// frame buffer not OK: probably means unsupported depth buffer format
-			llerrs << "Framebuffer Incomplete Dimensions." << llendl;
+			llerrs << "Framebuffer Incomplete Missing Attachment." << llendl;
 			break;
-		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
 			// frame buffer not OK: probably means unsupported depth buffer format
 			llerrs << "Framebuffer Incomplete Attachment." << llendl;
 			break; 
-		case GL_FRAMEBUFFER_UNSUPPORTED_EXT:                    
+		case GL_FRAMEBUFFER_UNSUPPORTED:                    
 			/* choose different formats */                        
 			llerrs << "Framebuffer unsupported." << llendl;
 			break;                                                
@@ -5484,8 +6077,6 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
 		tc2 /= (F32) res_mod;
 	}
 
-	gGL.setColorMask(true, true);
-		
 	LLFastTimer ftm(FTM_RENDER_BLOOM);
 	gGL.color4f(1,1,1,1);
 	LLGLDepthTest depth(GL_FALSE);
@@ -5681,7 +6272,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
 	if (LLRenderTarget::sUseFBO)
 	{
 		LLFastTimer ftm(FTM_RENDER_BLOOM_FBO);
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 	}
 
 	gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft;
@@ -5697,16 +6288,149 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
 	
 	LLVertexBuffer::unbind();
 
-	if (LLPipeline::sRenderDeferred && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2)
+	if (LLPipeline::sRenderDeferred && !LLViewerCamera::getInstance()->cameraUnderWater())
 	{
+		bool dof_enabled = true;
+
+		LLGLSLShader* shader = &gDeferredPostProgram;
+		if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2)
+		{
+			shader = &gDeferredGIFinalProgram;
+			dof_enabled = false;
+		}
+		else if (LLToolMgr::getInstance()->inBuildMode() || !gSavedSettings.getBOOL("RenderDepthOfField"))
+		{ //squish focal length when in build mode (or if DoF is disabled) so DoF doesn't make editing objects difficult
+			shader = &gDeferredPostNoDoFProgram;
+			dof_enabled = false;
+		}
+		
+		
 		LLGLDisable blend(GL_BLEND);
-		bindDeferredShader(gDeferredGIFinalProgram);
+		bindDeferredShader(*shader);
+
+		if (dof_enabled)
+		{
+			//depth of field focal plane calculations
+
+			static F32 current_distance = 16.f;
+			static F32 start_distance = 16.f;
+			static F32 transition_time = 1.f;
+
+			LLVector3 focus_point;
+
+			LLViewerObject* obj = LLViewerMediaFocus::getInstance()->getFocusedObject();
+			if (obj && obj->mDrawable && obj->isSelected())
+			{ //focus on selected media object
+				S32 face_idx = LLViewerMediaFocus::getInstance()->getFocusedFace();
+				if (obj && obj->mDrawable)
+				{
+					LLFace* face = obj->mDrawable->getFace(face_idx);
+					if (face)
+					{
+						focus_point = face->getPositionAgent();
+					}
+				}
+			}
+		
+			if (focus_point.isExactlyZero())
+			{
+				if (LLViewerJoystick::getInstance()->getOverrideCamera())
+				{ //focus on point under cursor
+					focus_point = gDebugRaycastIntersection;
+				}
+				else if (gAgentCamera.cameraMouselook())
+				{ //focus on point under mouselook crosshairs
+					gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE,
+												  NULL,
+												  &focus_point);
+				}
+				else
+				{
+					LLViewerObject* obj = gAgentCamera.getFocusObject();
+					if (obj)
+					{ //focus on alt-zoom target
+						focus_point = LLVector3(gAgentCamera.getFocusGlobal()-gAgent.getRegion()->getOriginGlobal());
+					}
+					else
+					{ //focus on your avatar
+						focus_point = gAgent.getPositionAgent();
+					}
+				}
+			}
+
+			LLVector3 eye = LLViewerCamera::getInstance()->getOrigin();
+			F32 target_distance = 16.f;
+			if (!focus_point.isExactlyZero())
+			{
+				target_distance = LLViewerCamera::getInstance()->getAtAxis() * (focus_point-eye);
+			}
+
+			if (transition_time >= 1.f &&
+				fabsf(current_distance-target_distance)/current_distance > 0.01f)
+			{ //large shift happened, interpolate smoothly to new target distance
+				transition_time = 0.f;
+				start_distance = current_distance;
+			}
+			else if (transition_time < 1.f)
+			{ //currently in a transition, continue interpolating
+				transition_time += 1.f/gSavedSettings.getF32("CameraFocusTransitionTime")*gFrameIntervalSeconds;
+				transition_time = llmin(transition_time, 1.f);
+
+				F32 t = cosf(transition_time*F_PI+F_PI)*0.5f+0.5f;
+				current_distance = start_distance + (target_distance-start_distance)*t;
+			}
+			else
+			{ //small or no change, just snap to target distance
+				current_distance = target_distance;
+			}
+
+			//convert to mm
+			F32 subject_distance = current_distance*1000.f;
+			F32 fnumber = gSavedSettings.getF32("CameraFNumber");
+			F32 default_focal_length = gSavedSettings.getF32("CameraFocalLength");
+
+			F32 fov = LLViewerCamera::getInstance()->getView();
+		
+			const F32 default_fov = gSavedSettings.getF32("CameraFieldOfView") * F_PI/180.f;
+			//const F32 default_aspect_ratio = gSavedSettings.getF32("CameraAspectRatio");
+		
+			//F32 aspect_ratio = (F32) mScreen.getWidth()/(F32)mScreen.getHeight();
+		
+			F32 dv = 2.f*default_focal_length * tanf(default_fov/2.f);
+			//F32 dh = 2.f*default_focal_length * tanf(default_fov*default_aspect_ratio/2.f);
+
+			F32 focal_length = dv/(2*tanf(fov/2.f));
+		 
+			//F32 tan_pixel_angle = tanf(LLDrawable::sCurPixelAngle);
+	
+			// from wikipedia -- c = |s2-s1|/s2 * f^2/(N(S1-f))
+			// where	 N = fnumber
+			//			 s2 = dot distance
+			//			 s1 = subject distance
+			//			 f = focal length
+			//	
 
-		S32 channel = gDeferredGIFinalProgram.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE);
+			F32 blur_constant = focal_length*focal_length/(fnumber*(subject_distance-focal_length));
+			blur_constant /= 1000.f; //convert to meters for shader
+			F32 magnification = focal_length/(subject_distance-focal_length);
+
+			shader->uniform1f("focal_distance", -subject_distance/1000.f);
+			shader->uniform1f("blur_constant", blur_constant);
+			shader->uniform1f("tan_pixel_angle", tanf(1.f/LLDrawable::sCurPixelAngle));
+			shader->uniform1f("magnification", magnification);
+		}
+
+		S32 channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE);
 		if (channel > -1)
 		{
 			mScreen.bindTexture(0, channel);
+			gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
 		}
+		//channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE);
+		//if (channel > -1)
+		//{
+			//gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
+		//}
 
 		gGL.begin(LLRender::TRIANGLE_STRIP);
 		gGL.texCoord2f(tc1.mV[0], tc1.mV[1]);
@@ -5720,11 +6444,10 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
 		
 		gGL.end();
 
-		unbindDeferredShader(gDeferredGIFinalProgram);
+		unbindDeferredShader(*shader);
 	}
 	else
 	{
-
 		if (res_mod > 1)
 		{
 			tc2 /= (F32) res_mod;
@@ -5772,26 +6495,51 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
 		gGL.getTexUnit(1)->bind(&mScreen);
 		gGL.getTexUnit(1)->activate();
 		
-		LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+		LLGLEnable multisample(gSavedSettings.getU32("RenderFSAASamples") > 0 ? GL_MULTISAMPLE_ARB : 0);
+		
+		buff->setBuffer(mask);
+		buff->drawArrays(LLRender::TRIANGLE_STRIP, 0, 3);
+		
+		gGL.getTexUnit(1)->disable();
+		gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT);
+
+		gGL.getTexUnit(0)->activate();
+		gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
+	}
+
+	if (LLRenderTarget::sUseFBO)
+	{ //copy depth buffer from mScreen to framebuffer
+		LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), 
+			0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+	}
+	
+	gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+	if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PHYSICS_SHAPES))
+	{
+		LLVector2 tc1(0,0);
+		LLVector2 tc2((F32) gViewerWindow->getWorldViewWidthRaw()*2,
+				  (F32) gViewerWindow->getWorldViewHeightRaw()*2);
+
+		LLGLEnable blend(GL_BLEND);
+		gGL.color4f(1,1,1,0.75f);
+
+		gGL.getTexUnit(0)->bind(&mPhysicsDisplay);
+
+		gGL.begin(LLRender::TRIANGLE_STRIP);
+		gGL.texCoord2f(tc1.mV[0], tc1.mV[1]);
+		gGL.vertex2f(-1,-1);
+		
+		gGL.texCoord2f(tc1.mV[0], tc2.mV[1]);
+		gGL.vertex2f(-1,3);
 		
-		buff->setBuffer(mask);
-		buff->drawArrays(LLRender::TRIANGLE_STRIP, 0, 3);
+		gGL.texCoord2f(tc2.mV[0], tc1.mV[1]);
+		gGL.vertex2f(3,-1);
 		
-		gGL.getTexUnit(1)->disable();
-		gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT);
-
-		gGL.getTexUnit(0)->activate();
-		gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
-
-		if (LLRenderTarget::sUseFBO)
-		{ //copy depth buffer from mScreen to framebuffer
-			LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), 
-				0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);
-		}
+		gGL.end();
+		gGL.flush();
 	}
-	
 
-	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 	glMatrixMode(GL_PROJECTION);
 	glPopMatrix();
 	glMatrixMode(GL_MODELVIEW);
@@ -6174,6 +6922,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen
 	shader.uniform2f("proj_shadow_res", mShadow[4].getWidth(), mShadow[4].getHeight());
 	shader.uniform1f("depth_cutoff", gSavedSettings.getF32("RenderEdgeDepthCutoff"));
 	shader.uniform1f("norm_cutoff", gSavedSettings.getF32("RenderEdgeNormCutoff"));
+	
 
 	if (shader.getUniformLocation("norm_mat") >= 0)
 	{
@@ -6211,7 +6960,7 @@ void LLPipeline::renderDeferredLighting()
 							0, 0, mDeferredDepth.getWidth(), mDeferredDepth.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);	
 		}
 
-		LLGLEnable multisample(GL_MULTISAMPLE_ARB);
+		LLGLEnable multisample(gSavedSettings.getU32("RenderFSAASamples") > 0 ? GL_MULTISAMPLE_ARB : 0);
 
 		if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD))
 		{
@@ -6250,16 +6999,15 @@ void LLPipeline::renderDeferredLighting()
 			glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], 0);
 		}
 
-			glPushMatrix();
-			glLoadIdentity();
-			glMatrixMode(GL_PROJECTION);
-			glPushMatrix();
-			glLoadIdentity();
-
-			mDeferredLight[0].bindTarget();
+		glPushMatrix();
+		glLoadIdentity();
+		glMatrixMode(GL_PROJECTION);
+		glPushMatrix();
+		glLoadIdentity();
 
 		if (gSavedSettings.getBOOL("RenderDeferredSSAO") || gSavedSettings.getS32("RenderShadowDetail") > 0)
 		{
+			mDeferredLight[0].bindTarget();
 			{ //paint shadow/SSAO light map (direct lighting lightmap)
 				LLFastTimer ftm(FTM_SUN_SHADOW);
 				bindDeferredShader(gDeferredSunProgram, 0);
@@ -6300,16 +7048,9 @@ void LLPipeline::renderDeferredLighting()
 				
 				unbindDeferredShader(gDeferredSunProgram);
 			}
-		}
-			else
-			{
-			glClearColor(1,1,1,1);
-				mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT);
-			glClearColor(0,0,0,0);
-			}
-
 			mDeferredLight[0].flush();
-
+		}
+		
 		{ //global illumination specific block (still experimental)
 			if (gSavedSettings.getBOOL("RenderDeferredBlurLight") &&
 			    gSavedSettings.getBOOL("RenderDeferredGI"))
@@ -6352,7 +7093,7 @@ void LLPipeline::renderDeferredLighting()
 						mLuminanceMap.flush();
 						gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLuminanceMap.getTexture(), true);
 						gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR);
-						glGenerateMipmapEXT(GL_TEXTURE_2D);
+						glGenerateMipmap(GL_TEXTURE_2D);
 					}
 				}
 
@@ -6415,75 +7156,74 @@ void LLPipeline::renderDeferredLighting()
 		}
 
 		if (gSavedSettings.getBOOL("RenderDeferredSSAO"))
-			{ //soften direct lighting lightmap
-				LLFastTimer ftm(FTM_SOFTEN_SHADOW);
-				//blur lightmap
-				mDeferredLight[1].bindTarget();
+		{ //soften direct lighting lightmap
+			LLFastTimer ftm(FTM_SOFTEN_SHADOW);
+			//blur lightmap
+			mDeferredLight[1].bindTarget();
 
-				glClearColor(1,1,1,1);
-				mDeferredLight[1].clear(GL_COLOR_BUFFER_BIT);
-				glClearColor(0,0,0,0);
-				
-				bindDeferredShader(gDeferredBlurLightProgram);
+			glClearColor(1,1,1,1);
+			mDeferredLight[1].clear(GL_COLOR_BUFFER_BIT);
+			glClearColor(0,0,0,0);
+			
+			bindDeferredShader(gDeferredBlurLightProgram);
 
-				LLVector3 go = gSavedSettings.getVector3("RenderShadowGaussian");
-				const U32 kern_length = 4;
-				F32 blur_size = gSavedSettings.getF32("RenderShadowBlurSize");
-				F32 dist_factor = gSavedSettings.getF32("RenderShadowBlurDistFactor");
+			LLVector3 go = gSavedSettings.getVector3("RenderShadowGaussian");
+			const U32 kern_length = 4;
+			F32 blur_size = gSavedSettings.getF32("RenderShadowBlurSize");
+			F32 dist_factor = gSavedSettings.getF32("RenderShadowBlurDistFactor");
 
-				// sample symmetrically with the middle sample falling exactly on 0.0
-				F32 x = 0.f;
+			// sample symmetrically with the middle sample falling exactly on 0.0
+			F32 x = 0.f;
 
-				LLVector3 gauss[32]; // xweight, yweight, offset
+			LLVector3 gauss[32]; // xweight, yweight, offset
 
-				for (U32 i = 0; i < kern_length; i++)
-				{
-					gauss[i].mV[0] = llgaussian(x, go.mV[0]);
-					gauss[i].mV[1] = llgaussian(x, go.mV[1]);
-					gauss[i].mV[2] = x;
-					x += 1.f;
-				}
+			for (U32 i = 0; i < kern_length; i++)
+			{
+				gauss[i].mV[0] = llgaussian(x, go.mV[0]);
+				gauss[i].mV[1] = llgaussian(x, go.mV[1]);
+				gauss[i].mV[2] = x;
+				x += 1.f;
+			}
 
-				gDeferredBlurLightProgram.uniform2f("delta", 1.f, 0.f);
-				gDeferredBlurLightProgram.uniform1f("dist_factor", dist_factor);
-				gDeferredBlurLightProgram.uniform3fv("kern[0]", kern_length, gauss[0].mV);
-				gDeferredBlurLightProgram.uniform3fv("kern", kern_length, gauss[0].mV);
-				gDeferredBlurLightProgram.uniform1f("kern_scale", blur_size * (kern_length/2.f - 0.5f));
+			gDeferredBlurLightProgram.uniform2f("delta", 1.f, 0.f);
+			gDeferredBlurLightProgram.uniform1f("dist_factor", dist_factor);
+			gDeferredBlurLightProgram.uniform3fv("kern", kern_length, gauss[0].mV);
+			gDeferredBlurLightProgram.uniform1f("kern_scale", blur_size * (kern_length/2.f - 0.5f));
+		
+			{
+				LLGLDisable blend(GL_BLEND);
+				LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS);
+				stop_glerror();
+				glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
+				stop_glerror();
+			}
 			
-				{
-					LLGLDisable blend(GL_BLEND);
-					LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS);
-					stop_glerror();
-					glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
-					stop_glerror();
-				}
-				
-				mDeferredLight[1].flush();
-				unbindDeferredShader(gDeferredBlurLightProgram);
+			mDeferredLight[1].flush();
+			unbindDeferredShader(gDeferredBlurLightProgram);
 
-				bindDeferredShader(gDeferredBlurLightProgram, 1);
-				mDeferredLight[0].bindTarget();
+			bindDeferredShader(gDeferredBlurLightProgram, 1);
+			mDeferredLight[0].bindTarget();
 
-				gDeferredBlurLightProgram.uniform2f("delta", 0.f, 1.f);
+			gDeferredBlurLightProgram.uniform2f("delta", 0.f, 1.f);
 
-				{
-					LLGLDisable blend(GL_BLEND);
-					LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS);
-					stop_glerror();
-					glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
-					stop_glerror();
-				}
-				mDeferredLight[0].flush();
-				unbindDeferredShader(gDeferredBlurLightProgram);
+			{
+				LLGLDisable blend(GL_BLEND);
+				LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS);
+				stop_glerror();
+				glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
+				stop_glerror();
 			}
+			mDeferredLight[0].flush();
+			unbindDeferredShader(gDeferredBlurLightProgram);
+		}
 
-			stop_glerror();
-			glPopMatrix();
-			stop_glerror();
-			glMatrixMode(GL_MODELVIEW);
-			stop_glerror();
-			glPopMatrix();
-			stop_glerror();
+		stop_glerror();
+		glPopMatrix();
+		stop_glerror();
+		glMatrixMode(GL_MODELVIEW);
+		stop_glerror();
+		glPopMatrix();
+		stop_glerror();
 
 		//copy depth and stencil from deferred screen
 		//mScreen.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(),
@@ -6549,10 +7289,8 @@ void LLPipeline::renderDeferredLighting()
 			gPipeline.popRenderTypeMask();
 		}
 
-		BOOL render_local = gSavedSettings.getBOOL("RenderDeferredLocalLights");
-		BOOL render_fullscreen = gSavedSettings.getBOOL("RenderDeferredFullscreenLights");
-		
-
+		BOOL render_local = gSavedSettings.getBOOL("RenderLocalLights");
+				
 		if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2)
 		{
 			mDeferredLight[1].flush();
@@ -6560,7 +7298,7 @@ void LLPipeline::renderDeferredLighting()
 			mDeferredLight[2].clear(GL_COLOR_BUFFER_BIT);
 		}
 
-		if (render_local || render_fullscreen)
+		if (render_local)
 		{
 			gGL.setSceneBlendType(LLRender::BT_ADD);
 			std::list<LLVector4> fullscreen_lights;
@@ -6574,10 +7312,11 @@ void LLPipeline::renderDeferredLighting()
 
 			std::list<LLVector4> light_colors;
 
+			LLVertexBuffer::unbind();
+
 			F32 v[24];
 			glVertexPointer(3, GL_FLOAT, 0, v);
-			BOOL render_local = gSavedSettings.getBOOL("RenderDeferredLocalLights");
-
+			
 			{
 				bindDeferredShader(gDeferredLightProgram);
 				LLGLDepthTest depth(GL_TRUE, GL_FALSE);
@@ -6600,8 +7339,9 @@ void LLPipeline::renderDeferredLighting()
 					}
 
 
-					LLVector3 center = drawablep->getPositionAgent();
-					F32* c = center.mV;
+					LLVector4a center;
+					center.load3(drawablep->getPositionAgent().mV);
+					const F32* c = center.getF32ptr();
 					F32 s = volume->getLightRadius()*1.5f;
 
 					LLColor3 col = volume->getLightColor();
@@ -6617,7 +7357,9 @@ void LLPipeline::renderDeferredLighting()
 						continue;
 					}
 
-					if (camera->AABBInFrustumNoFarClip(center, LLVector3(s,s,s)) == 0)
+					LLVector4a sa;
+					sa.splat(s);
+					if (camera->AABBInFrustumNoFarClip(center, sa) == 0)
 					{
 						continue;
 					}
@@ -6661,11 +7403,11 @@ void LLPipeline::renderDeferredLighting()
 							glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s);
 							glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f);
 							glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8,
-								GL_UNSIGNED_BYTE, get_box_fan_indices(camera, center));
+								GL_UNSIGNED_BYTE, get_box_fan_indices_ptr(camera, center));
 							stop_glerror();
 						}
 					}
-					else if (render_fullscreen)
+					else
 					{	
 						if (volume->isLightSpotlight())
 						{
@@ -6695,8 +7437,9 @@ void LLPipeline::renderDeferredLighting()
 
 					LLVOVolume* volume = drawablep->getVOVolume();
 
-					LLVector3 center = drawablep->getPositionAgent();
-					F32* c = center.mV;
+					LLVector4a center;
+					center.load3(drawablep->getPositionAgent().mV);
+					const F32* c = center.getF32ptr();
 					F32 s = volume->getLightRadius()*1.5f;
 
 					sVisibleLightCount++;
@@ -6726,7 +7469,7 @@ void LLPipeline::renderDeferredLighting()
 					glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s);
 					glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f);
 					glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8,
-							GL_UNSIGNED_BYTE, get_box_fan_indices(camera, center));
+							GL_UNSIGNED_BYTE, get_box_fan_indices_ptr(camera, center));
 				}
 				gDeferredSpotLightProgram.disableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION);
 				unbindDeferredShader(gDeferredSpotLightProgram);
@@ -6768,9 +7511,7 @@ void LLPipeline::renderDeferredLighting()
 					if (count == max_count || fullscreen_lights.empty())
 					{
 						gDeferredMultiLightProgram.uniform1i("light_count", count);
-						gDeferredMultiLightProgram.uniform4fv("light[0]", count, (GLfloat*) light);
 						gDeferredMultiLightProgram.uniform4fv("light", count, (GLfloat*) light);
-						gDeferredMultiLightProgram.uniform4fv("light_col[0]", count, (GLfloat*) col);
 						gDeferredMultiLightProgram.uniform4fv("light_col", count, (GLfloat*) col);
 						gDeferredMultiLightProgram.uniform1f("far_z", far_z);
 						far_z = 0.f;
@@ -7138,11 +7879,6 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 		LLCamera camera = camera_in;
 		camera.setFar(camera.getFar()*0.87654321f);
 		LLPipeline::sReflectionRender = TRUE;
-		S32 occlusion = LLPipeline::sUseOcclusion;
-
-		LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD;
-
-		LLPipeline::sUseOcclusion = llmin(occlusion, 1);
 		
 		gPipeline.pushRenderTypeMask();
 
@@ -7178,22 +7914,27 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 
 		if (!LLViewerCamera::getInstance()->cameraUnderWater())
 		{	//generate planar reflection map
+
+			//disable occlusion culling for reflection map for now
+			S32 occlusion = LLPipeline::sUseOcclusion;
+			LLPipeline::sUseOcclusion = 0;
 			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 			glClearColor(0,0,0,0);
 			mWaterRef.bindTarget();
+			LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WATER0;
 			gGL.setColorMask(true, true);
 			mWaterRef.clear();
 			gGL.setColorMask(true, false);
 
 			mWaterRef.getViewport(gGLViewport);
-			
+
 			stop_glerror();
 
 			glPushMatrix();
 
 			mat.set_scale(glh::vec3f(1,1,-1));
 			mat.set_translate(glh::vec3f(0,0,height*2.f));
-			
+
 			glh::matrix4f current = glh_get_current_modelview();
 
 			mat = current * mat;
@@ -7213,47 +7954,46 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 			glCullFace(GL_FRONT);
 
 			static LLCullResult ref_result;
-		
+
 			if (LLDrawPoolWater::sNeedsDistortionUpdate)
 			{
 				//initial sky pass (no user clip plane)
 				{ //mask out everything but the sky
 					gPipeline.pushRenderTypeMask();
 					gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY,
-												LLPipeline::RENDER_TYPE_WL_SKY,
-												LLPipeline::END_RENDER_TYPES);
+						LLPipeline::RENDER_TYPE_WL_SKY,
+						LLPipeline::RENDER_TYPE_CLOUDS,
+						LLPipeline::END_RENDER_TYPES);
+
 					static LLCullResult result;
 					updateCull(camera, result);
 					stateSort(camera, result);
-					andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY,
-										LLPipeline::RENDER_TYPE_CLOUDS,
-										LLPipeline::RENDER_TYPE_WL_SKY,
-										LLPipeline::END_RENDER_TYPES);
 
 					renderGeom(camera, TRUE);
+
 					gPipeline.popRenderTypeMask();
 				}
 
 				gPipeline.pushRenderTypeMask();
 
 				clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER,
-									LLPipeline::RENDER_TYPE_VOIDWATER,
-									LLPipeline::RENDER_TYPE_GROUND,
-									LLPipeline::RENDER_TYPE_SKY,
-									LLPipeline::RENDER_TYPE_CLOUDS,
-									LLPipeline::END_RENDER_TYPES);	
+					LLPipeline::RENDER_TYPE_VOIDWATER,
+					LLPipeline::RENDER_TYPE_GROUND,
+					LLPipeline::RENDER_TYPE_SKY,
+					LLPipeline::RENDER_TYPE_CLOUDS,
+					LLPipeline::END_RENDER_TYPES);	
 
-					S32 detail = gSavedSettings.getS32("RenderReflectionDetail");
+				S32 detail = gSavedSettings.getS32("RenderReflectionDetail");
 				if (detail > 0)
 				{ //mask out selected geometry based on reflection detail
 					if (detail < 4)
 					{
 						clearRenderTypeMask(LLPipeline::RENDER_TYPE_PARTICLES, END_RENDER_TYPES);
-					if (detail < 3)
-					{
-							clearRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES);
-						if (detail < 2)
+						if (detail < 3)
 						{
+							clearRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES);
+							if (detail < 2)
+							{
 								clearRenderTypeMask(LLPipeline::RENDER_TYPE_VOLUME, END_RENDER_TYPES);
 							}
 						}
@@ -7261,19 +8001,19 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 
 					LLGLUserClipPlane clip_plane(plane, mat, projection);
 					LLGLDisable cull(GL_CULL_FACE);
-					updateCull(camera, ref_result, 1);
+					updateCull(camera, ref_result, -water_clip, &plane);
 					stateSort(camera, ref_result);
-				}
-				
-			if (LLDrawPoolWater::sNeedsDistortionUpdate)
-			{
-					if (gSavedSettings.getS32("RenderReflectionDetail") > 0)
+				}	
+
+				if (LLDrawPoolWater::sNeedsDistortionUpdate)
 				{
-					gPipeline.grabReferences(ref_result);
-					LLGLUserClipPlane clip_plane(plane, mat, projection);
-					renderGeom(camera);
-				}
-			}	
+					if (gSavedSettings.getS32("RenderReflectionDetail") > 0)
+					{
+						gPipeline.grabReferences(ref_result);
+						LLGLUserClipPlane clip_plane(plane, mat, projection);
+						renderGeom(camera);
+					}
+				}	
 
 				gPipeline.popRenderTypeMask();
 			}	
@@ -7281,6 +8021,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 			glPopMatrix();
 			mWaterRef.flush();
 			glh_set_current_modelview(current);
+			LLPipeline::sUseOcclusion = occlusion;
 		}
 
 		camera.setOrigin(camera_in.getOrigin());
@@ -7311,15 +8052,18 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 			LLColor4& col = LLDrawPoolWater::sWaterFogColor;
 			glClearColor(col.mV[0], col.mV[1], col.mV[2], 0.f);
 			mWaterDis.bindTarget();
+			LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WATER1;
 			mWaterDis.getViewport(gGLViewport);
 			
 			if (!LLPipeline::sUnderWaterRender || LLDrawPoolWater::sNeedsReflectionUpdate)
 			{
 				//clip out geometry on the same side of water as the camera
 				mat = glh_get_current_modelview();
-				LLGLUserClipPlane clip_plane(LLPlane(-pnorm, -(pd+pad)), mat, projection);
+				LLPlane plane(-pnorm, -(pd+pad));
+
+				LLGLUserClipPlane clip_plane(plane, mat, projection);
 				static LLCullResult result;
-				updateCull(camera, result, water_clip);
+				updateCull(camera, result, water_clip, &plane);
 				stateSort(camera, result);
 
 				gGL.setColorMask(true, true);
@@ -7347,8 +8091,8 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 		gPipeline.popRenderTypeMask();
 		LLDrawPoolWater::sNeedsReflectionUpdate = FALSE;
 		LLDrawPoolWater::sNeedsDistortionUpdate = FALSE;
-		LLViewerCamera::getInstance()->setUserClipPlane(LLPlane(-pnorm, -pd));
-		LLPipeline::sUseOcclusion = occlusion;
+		LLPlane npnorm(-pnorm, -pd);
+		LLViewerCamera::getInstance()->setUserClipPlane(npnorm);
 		
 		LLGLState::checkStates();
 		LLGLState::checkTextureChannels();
@@ -7358,6 +8102,8 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
 		{
 			gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode());
 		}
+
+		LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD;
 	}
 }
 
@@ -7460,14 +8206,14 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
 	glLoadMatrixf(proj.m);
 	glMatrixMode(GL_MODELVIEW);
 	glPushMatrix();
-	glLoadMatrixf(view.m);
+	glLoadMatrixd(gGLModelView);
 
 	stop_glerror();
 	gGLLastMatrix = NULL;
 
 	{
-		LLGLDepthTest depth(GL_TRUE);
-		glClear(GL_DEPTH_BUFFER_BIT);
+		//LLGLDepthTest depth(GL_TRUE);
+		//glClear(GL_DEPTH_BUFFER_BIT);
 	}
 
 	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@@ -7480,6 +8226,8 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
 	
 	//glCullFace(GL_FRONT);
 
+	LLVertexBuffer::unbind();
+
 	{
 		LLFastTimer ftm(FTM_SHADOW_SIMPLE);
 		LLGLDisable test(GL_ALPHA_TEST);
@@ -7547,14 +8295,13 @@ BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 	}
 
 	//get set of planes on bounding box
-	std::vector<LLPlane> bp;
-		
-	bp.push_back(LLPlane(min, LLVector3(-1,0,0)));
-	bp.push_back(LLPlane(min, LLVector3(0,-1,0)));
-	bp.push_back(LLPlane(min, LLVector3(0,0,-1)));
-	bp.push_back(LLPlane(max, LLVector3(1,0,0)));
-	bp.push_back(LLPlane(max, LLVector3(0,1,0)));
-	bp.push_back(LLPlane(max, LLVector3(0,0,1)));
+	LLPlane bp[] = { 
+		LLPlane(min, LLVector3(-1,0,0)),
+		LLPlane(min, LLVector3(0,-1,0)),
+		LLPlane(min, LLVector3(0,0,-1)),
+		LLPlane(max, LLVector3(1,0,0)),
+		LLPlane(max, LLVector3(0,1,0)),
+		LLPlane(max, LLVector3(0,0,1))};
 	
 	//potential points
 	std::vector<LLVector3> pp;
@@ -7602,7 +8349,8 @@ BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 			const LLPlane& cp = camera.getAgentPlane(j);
 			const LLVector3& v1 = pp[bs[i*2+0]];
 			const LLVector3& v2 = pp[bs[i*2+1]];
-			const LLVector3 n(cp.mV);
+			LLVector3 n;
+			cp.getVector3(n);
 
 			LLVector3 line = v1-v2;
 
@@ -7616,8 +8364,8 @@ BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 				LLVector3 intersect = v2+line*t;
 				pp.push_back(intersect);
 			}
-			}
 		}
+	}
 			
 	//camera frustum line segments
 	const U32 fs[] =
@@ -7625,7 +8373,7 @@ BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 		0,1,
 		1,2,
 		2,3,
-		3,1,
+		3,0,
 
 		4,5,
 		5,6,
@@ -7648,7 +8396,8 @@ BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 			const LLVector3& v1 = pp[fs[i*2+0]+8];
 			const LLVector3& v2 = pp[fs[i*2+1]+8];
 			const LLPlane& cp = bp[j];
-			const LLVector3 n(cp.mV);
+			LLVector3 n;
+			cp.getVector3(n);
 
 			LLVector3 line = v1-v2;
 
@@ -7663,7 +8412,7 @@ BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector
 				pp.push_back(intersect);
 			}	
 		}
-				}
+	}
 
 	LLVector3 ext[] = { min-LLVector3(0.05f,0.05f,0.05f),
 		max+LLVector3(0.05f,0.05f,0.05f) };
@@ -8027,6 +8776,14 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 	LLVector3 n = gSavedSettings.getVector3("RenderShadowNearDist");
 	//F32 nearDist[] = { n.mV[0], n.mV[1], n.mV[2], n.mV[2] };
 
+	//put together a universal "near clip" plane for shadow frusta
+	LLPlane shadow_near_clip;
+	{
+		LLVector3 p = gAgent.getPositionAgent();
+		p += mSunDir * gSavedSettings.getF32("RenderFarClip")*2.f;
+		shadow_near_clip.setVec(p, mSunDir);
+	}
+
 	LLVector3 lightDir = -mSunDir;
 	lightDir.normVec();
 
@@ -8101,7 +8858,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 		near_clip = -max.mV[2];
 		F32 far_clip = -min.mV[2]*2.f;
 
-		far_clip = llmin(far_clip, 128.f);
+		//far_clip = llmin(far_clip, 128.f);
 		far_clip = llmin(far_clip, camera.getFar());
 
 		F32 range = far_clip-near_clip;
@@ -8391,11 +9148,6 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 					fovx = acos(fovx);
 					fovz = acos(fovz);
 
-					if (fovx > cutoff || llround(fovz, 0.01f) > cutoff)
-					{
-					//	llerrs << "WTF?" << llendl;
-					}
-
 					mShadowFOV.mV[j] = cutoff;
 				}
 
@@ -8451,7 +9203,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 			}
 		}
 
-		shadow_cam.setFar(128.f);
+		//shadow_cam.setFar(128.f);
 		shadow_cam.setOriginAndLookAt(eye, up, center);
 
 		shadow_cam.setOrigin(0,0,0);
@@ -8461,7 +9213,8 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 
 		LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE);
 
-		shadow_cam.ignoreAgentFrustumPlane(LLCamera::AGENT_PLANE_NEAR);
+		//shadow_cam.ignoreAgentFrustumPlane(LLCamera::AGENT_PLANE_NEAR);
+		shadow_cam.getAgentPlane(LLCamera::AGENT_PLANE_NEAR).set(shadow_near_clip);
 
 		//translate and scale to from [-1, 1] to [0, 1]
 		glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f,
@@ -8488,7 +9241,8 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 
 		mShadow[j].bindTarget();
 		mShadow[j].getViewport(gGLViewport);
-
+		mShadow[j].clear();
+		
 		{
 			static LLCullResult result[4];
 
@@ -8507,152 +9261,138 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 
 	
 	//hack to disable projector shadows 
-	static bool clear = true;
 	bool gen_shadow = gSavedSettings.getS32("RenderShadowDetail") > 1;
 
 	if (gen_shadow)
 	{
-		clear = true;
-	F32 fade_amt = gFrameIntervalSeconds * llmax(LLViewerCamera::getInstance()->getVelocityStat()->getCurrentPerSec(), 1.f);
-
-	//update shadow targets
-	for (U32 i = 0; i < 2; i++)
-	{ //for each current shadow
-		LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW4+i;
+		F32 fade_amt = gFrameIntervalSeconds * llmax(LLViewerCamera::getInstance()->getVelocityStat()->getCurrentPerSec(), 1.f);
 
-		if (mShadowSpotLight[i].notNull() && 
-			(mShadowSpotLight[i] == mTargetShadowSpotLight[0] ||
-			mShadowSpotLight[i] == mTargetShadowSpotLight[1]))
-		{ //keep this spotlight
-			mSpotLightFade[i] = llmin(mSpotLightFade[i]+fade_amt, 1.f);
-		}
-		else
-		{ //fade out this light
-			mSpotLightFade[i] = llmax(mSpotLightFade[i]-fade_amt, 0.f);
-			
-			if (mSpotLightFade[i] == 0.f || mShadowSpotLight[i].isNull())
-			{ //faded out, grab one of the pending spots (whichever one isn't already taken)
-				if (mTargetShadowSpotLight[0] != mShadowSpotLight[(i+1)%2])
-				{
-					mShadowSpotLight[i] = mTargetShadowSpotLight[0];
-				}
-				else
-				{
-					mShadowSpotLight[i] = mTargetShadowSpotLight[1];
+		//update shadow targets
+		for (U32 i = 0; i < 2; i++)
+		{ //for each current shadow
+			LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW4+i;
+
+			if (mShadowSpotLight[i].notNull() && 
+				(mShadowSpotLight[i] == mTargetShadowSpotLight[0] ||
+				mShadowSpotLight[i] == mTargetShadowSpotLight[1]))
+			{ //keep this spotlight
+				mSpotLightFade[i] = llmin(mSpotLightFade[i]+fade_amt, 1.f);
+			}
+			else
+			{ //fade out this light
+				mSpotLightFade[i] = llmax(mSpotLightFade[i]-fade_amt, 0.f);
+				
+				if (mSpotLightFade[i] == 0.f || mShadowSpotLight[i].isNull())
+				{ //faded out, grab one of the pending spots (whichever one isn't already taken)
+					if (mTargetShadowSpotLight[0] != mShadowSpotLight[(i+1)%2])
+					{
+						mShadowSpotLight[i] = mTargetShadowSpotLight[0];
+					}
+					else
+					{
+						mShadowSpotLight[i] = mTargetShadowSpotLight[1];
+					}
 				}
 			}
 		}
-	}
-
-	for (S32 i = 0; i < 2; i++)
-	{
-		glh_set_current_modelview(saved_view);
-		glh_set_current_projection(saved_proj);
 
-		if (mShadowSpotLight[i].isNull())
+		for (S32 i = 0; i < 2; i++)
 		{
-			continue;
-		}
+			glh_set_current_modelview(saved_view);
+			glh_set_current_projection(saved_proj);
 
-		LLVOVolume* volume = mShadowSpotLight[i]->getVOVolume();
+			if (mShadowSpotLight[i].isNull())
+			{
+				continue;
+			}
 
-		if (!volume)
-		{
-			mShadowSpotLight[i] = NULL;
-			continue;
-		}
+			LLVOVolume* volume = mShadowSpotLight[i]->getVOVolume();
+
+			if (!volume)
+			{
+				mShadowSpotLight[i] = NULL;
+				continue;
+			}
 
-		LLDrawable* drawable = mShadowSpotLight[i];
+			LLDrawable* drawable = mShadowSpotLight[i];
 
-		LLVector3 params = volume->getSpotLightParams();
-		F32 fov = params.mV[0];
+			LLVector3 params = volume->getSpotLightParams();
+			F32 fov = params.mV[0];
 
-		//get agent->light space matrix (modelview)
-		LLVector3 center = drawable->getPositionAgent();
-		LLQuaternion quat = volume->getRenderRotation();
+			//get agent->light space matrix (modelview)
+			LLVector3 center = drawable->getPositionAgent();
+			LLQuaternion quat = volume->getRenderRotation();
 
-		//get near clip plane
-		LLVector3 scale = volume->getScale();
-		LLVector3 at_axis(0,0,-scale.mV[2]*0.5f);
-		at_axis *= quat;
+			//get near clip plane
+			LLVector3 scale = volume->getScale();
+			LLVector3 at_axis(0,0,-scale.mV[2]*0.5f);
+			at_axis *= quat;
 
-		LLVector3 np = center+at_axis;
-		at_axis.normVec();
+			LLVector3 np = center+at_axis;
+			at_axis.normVec();
 
-		//get origin that has given fov for plane np, at_axis, and given scale
-		F32 dist = (scale.mV[1]*0.5f)/tanf(fov*0.5f);
+			//get origin that has given fov for plane np, at_axis, and given scale
+			F32 dist = (scale.mV[1]*0.5f)/tanf(fov*0.5f);
 
-		LLVector3 origin = np - at_axis*dist;
+			LLVector3 origin = np - at_axis*dist;
 
-		LLMatrix4 mat(quat, LLVector4(origin, 1.f));
+			LLMatrix4 mat(quat, LLVector4(origin, 1.f));
 
-		view[i+4] = glh::matrix4f((F32*) mat.mMatrix);
+			view[i+4] = glh::matrix4f((F32*) mat.mMatrix);
 
-		view[i+4] = view[i+4].inverse();
+			view[i+4] = view[i+4].inverse();
 
-		//get perspective matrix
-		F32 near_clip = dist+0.01f;
-		F32 width = scale.mV[VX];
-		F32 height = scale.mV[VY];
-		F32 far_clip = dist+volume->getLightRadius()*1.5f;
+			//get perspective matrix
+			F32 near_clip = dist+0.01f;
+			F32 width = scale.mV[VX];
+			F32 height = scale.mV[VY];
+			F32 far_clip = dist+volume->getLightRadius()*1.5f;
 
-		F32 fovy = fov * RAD_TO_DEG;
-		F32 aspect = width/height;
-		
-		proj[i+4] = gl_perspective(fovy, aspect, near_clip, far_clip);
+			F32 fovy = fov * RAD_TO_DEG;
+			F32 aspect = width/height;
+			
+			proj[i+4] = gl_perspective(fovy, aspect, near_clip, far_clip);
 
-		//translate and scale to from [-1, 1] to [0, 1]
-		glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f,
-						0.f, 0.5f, 0.f, 0.5f,
-						0.f, 0.f, 0.5f, 0.5f,
-						0.f, 0.f, 0.f, 1.f);
+			//translate and scale to from [-1, 1] to [0, 1]
+			glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f,
+							0.f, 0.5f, 0.f, 0.5f,
+							0.f, 0.f, 0.5f, 0.5f,
+							0.f, 0.f, 0.f, 1.f);
 
-		glh_set_current_modelview(view[i+4]);
-		glh_set_current_projection(proj[i+4]);
+			glh_set_current_modelview(view[i+4]);
+			glh_set_current_projection(proj[i+4]);
 
-		mSunShadowMatrix[i+4] = trans*proj[i+4]*view[i+4]*inv_view;
-		
-		for (U32 j = 0; j < 16; j++)
-		{
-			gGLLastModelView[j] = mShadowModelview[i+4].m[j];
-			gGLLastProjection[j] = mShadowProjection[i+4].m[j];
-		}
+			mSunShadowMatrix[i+4] = trans*proj[i+4]*view[i+4]*inv_view;
+			
+			for (U32 j = 0; j < 16; j++)
+			{
+				gGLLastModelView[j] = mShadowModelview[i+4].m[j];
+				gGLLastProjection[j] = mShadowProjection[i+4].m[j];
+			}
 
-		mShadowModelview[i+4] = view[i+4];
-		mShadowProjection[i+4] = proj[i+4];
+			mShadowModelview[i+4] = view[i+4];
+			mShadowProjection[i+4] = proj[i+4];
 
-		LLCamera shadow_cam = camera;
-		shadow_cam.setFar(far_clip);
-		shadow_cam.setOrigin(origin);
+			LLCamera shadow_cam = camera;
+			shadow_cam.setFar(far_clip);
+			shadow_cam.setOrigin(origin);
 
-		LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE);
+			LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE);
 
-		stop_glerror();
+			stop_glerror();
 
-		mShadow[i+4].bindTarget();
-		mShadow[i+4].getViewport(gGLViewport);
+			mShadow[i+4].bindTarget();
+			mShadow[i+4].getViewport(gGLViewport);
+			mShadow[i+4].clear();
 
-		static LLCullResult result[2];
+			static LLCullResult result[2];
 
-		LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+i+4;
+			LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+i+4;
 
-		renderShadow(view[i+4], proj[i+4], shadow_cam, result[i], FALSE, FALSE);
+			renderShadow(view[i+4], proj[i+4], shadow_cam, result[i], FALSE, FALSE);
 
-		mShadow[i+4].flush();
- 	}
-	}
-	else
-	{
-		if (clear)
-		{
-			clear = false;
-			for (U32 i = 4; i < 6; i++)
-			{
-				mShadow[i].bindTarget();
-				mShadow[i].clear();
-				mShadow[i].flush();
-			}
-		}
+			mShadow[i+4].flush();
+ 		}
 	}
 
 	if (!gSavedSettings.getBOOL("CameraOffset"))
@@ -8772,7 +9512,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 
 	stateSort(*LLViewerCamera::getInstance(), result);
 	
-	const LLVector3* ext = avatar->mDrawable->getSpatialExtents();
+	const LLVector4a* ext = avatar->mDrawable->getSpatialExtents();
 	LLVector3 pos(avatar->getRenderPosition()+avatar->getImpostorOffset());
 
 	LLCamera camera = *viewer_camera;
@@ -8781,25 +9521,30 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 	
 	LLVector2 tdim;
 
-	LLVector3 half_height = (ext[1]-ext[0])*0.5f;
 
-	LLVector3 left = camera.getLeftAxis();
-	left *= left;
-	left.normalize();
+	LLVector4a half_height;
+	half_height.setSub(ext[1], ext[0]);
+	half_height.mul(0.5f);
 
-	LLVector3 up = camera.getUpAxis();
-	up *= up;
-	up.normalize();
+	LLVector4a left;
+	left.load3(camera.getLeftAxis().mV);
+	left.mul(left);
+	left.normalize3fast();
 
-	tdim.mV[0] = fabsf(half_height * left);
-	tdim.mV[1] = fabsf(half_height * up);
+	LLVector4a up;
+	up.load3(camera.getUpAxis().mV);
+	up.mul(up);
+	up.normalize3fast();
+
+	tdim.mV[0] = fabsf(half_height.dot3(left).getF32());
+	tdim.mV[1] = fabsf(half_height.dot3(up).getF32());
 
 	glMatrixMode(GL_PROJECTION);
 	glPushMatrix();
-	//glh::matrix4f ortho = gl_ortho(-tdim.mV[0], tdim.mV[0], -tdim.mV[1], tdim.mV[1], 1.0, 256.0);
+	
 	F32 distance = (pos-camera.getOrigin()).length();
 	F32 fov = atanf(tdim.mV[1]/distance)*2.f*RAD_TO_DEG;
-	F32 aspect = tdim.mV[0]/tdim.mV[1]; //128.f/256.f;
+	F32 aspect = tdim.mV[0]/tdim.mV[1];
 	glh::matrix4f persp = gl_perspective(fov, aspect, 1.f, 256.f);
 	glh_set_current_projection(persp);
 	glLoadMatrixf(persp.m);
@@ -8816,9 +9561,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 
 	glClearColor(0.0f,0.0f,0.0f,0.0f);
 	gGL.setColorMask(true, true);
-	glStencilMask(0xFFFFFFFF);
-	glClearStencil(0);
-
+	
 	// get the number of pixels per angle
 	F32 pa = gViewerWindow->getWindowHeightRaw() / (RAD_TO_DEG * viewer_camera->getView());
 
@@ -8829,7 +9572,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 	if (!avatar->mImpostor.isComplete() || resX != avatar->mImpostor.getWidth() ||
 		resY != avatar->mImpostor.getHeight())
 	{
-		avatar->mImpostor.allocate(resX,resY,GL_RGBA,TRUE,TRUE);
+		avatar->mImpostor.allocate(resX,resY,GL_RGBA,TRUE,FALSE);
 		
 		if (LLPipeline::sRenderDeferred)
 		{
@@ -8841,43 +9584,30 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 	}
 
-	LLGLEnable stencil(GL_STENCIL_TEST);
-	glStencilMask(0xFFFFFFFF);
-	glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
-	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+	avatar->mImpostor.bindTarget();
 
-	{
-		LLGLEnable scissor(GL_SCISSOR_TEST);
-		glScissor(0, 0, resX, resY);
-		avatar->mImpostor.bindTarget();
-		avatar->mImpostor.clear();
-	}
-	
 	if (LLPipeline::sRenderDeferred)
 	{
-		stop_glerror();
+		avatar->mImpostor.clear();
 		renderGeomDeferred(camera);
 		renderGeomPostDeferred(camera);
 	}
 	else
 	{
+		LLGLEnable scissor(GL_SCISSOR_TEST);
+		glScissor(0, 0, resX, resY);
+		avatar->mImpostor.clear();
 		renderGeom(camera);
 	}
 	
-	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-	glStencilFunc(GL_EQUAL, 1, 0xFFFFFF);
-
-	{ //create alpha mask based on stencil buffer (grey out if muted)
-		LLVector3 left = camera.getLeftAxis()*tdim.mV[0]*2.f;
-		LLVector3 up = camera.getUpAxis()*tdim.mV[1]*2.f;
-
+	{ //create alpha mask based on depth buffer (grey out if muted)
 		if (LLPipeline::sRenderDeferred)
 		{
-			GLuint buff = GL_COLOR_ATTACHMENT0_EXT;
+			GLuint buff = GL_COLOR_ATTACHMENT0;
 			glDrawBuffersARB(1, &buff);
 		}
 
-		LLGLEnable blend(muted ? 0 : GL_BLEND);
+		LLGLDisable blend(GL_BLEND);
 
 		if (muted)
 		{
@@ -8888,25 +9618,34 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 			gGL.setColorMask(false, true);
 		}
 		
-		gGL.setSceneBlendType(LLRender::BT_ADD);
 		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 
-		LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+		LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_GREATER);
+
+		gGL.flush();
+
+		glPushMatrix();
+		glLoadIdentity();
+		glMatrixMode(GL_PROJECTION);
+		glPushMatrix();
+		glLoadIdentity();
+
+		static const F32 clip_plane = 0.99999f;
 
-		gGL.color4f(1,1,1,1);
 		gGL.color4ub(64,64,64,255);
 		gGL.begin(LLRender::QUADS);
-		gGL.vertex3fv((pos+left-up).mV);
-		gGL.vertex3fv((pos-left-up).mV);
-		gGL.vertex3fv((pos-left+up).mV);
-		gGL.vertex3fv((pos+left+up).mV);
+		gGL.vertex3f(-1, -1, clip_plane);
+		gGL.vertex3f(1, -1, clip_plane);
+		gGL.vertex3f(1, 1, clip_plane);
+		gGL.vertex3f(-1, 1, clip_plane);
 		gGL.end();
 		gGL.flush();
 
-		gGL.setSceneBlendType(LLRender::BT_ALPHA);
+		glPopMatrix();
+		glMatrixMode(GL_MODELVIEW);
+		glPopMatrix();
 	}
 
-
 	avatar->mImpostor.flush();
 
 	avatar->setImpostorDim(tdim);
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 92ae40ebb077a5e8dfd67179885997c18da31df4..e9a250cd6d7ba3c7a53d81922e1b25ccd61c7557 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -42,6 +42,10 @@
 
 #include <stack>
 
+#include <stack>
+
+#include <stack>
+
 class LLViewerTexture;
 class LLEdge;
 class LLFace;
@@ -54,6 +58,9 @@ class LLCubeMap;
 class LLCullResult;
 class LLVOAvatar;
 class LLGLSLShader;
+class LLCurlRequest;
+
+class LLMeshResponder;
 
 typedef enum e_avatar_skinning_method
 {
@@ -107,11 +114,11 @@ class LLPipeline
 	void resizeScreenTexture();
 	void releaseGLBuffers();
 	void createGLBuffers();
-	void allocateScreenBuffer(U32 resX, U32 resY);
 
+	void allocateScreenBuffer(U32 resX, U32 resY);
+	void allocatePhysicsBuffer();
+	
 	void resetVertexBuffers(LLDrawable* drawable);
-	void setUseVBO(BOOL use_vbo);
-	void setDisableVBOMapping(BOOL no_vbo_mapping);
 	void generateImpostor(LLVOAvatar* avatar);
 	void bindScreenToTexture();
 	void renderBloom(BOOL for_snapshot, F32 zoom_factor = 1.f, int subfield = 0);
@@ -201,7 +208,7 @@ class LLPipeline
 	BOOL visibleObjectsInFrustum(LLCamera& camera);
 	BOOL getVisibleExtents(LLCamera& camera, LLVector3 &min, LLVector3& max);
 	BOOL getVisiblePointCloud(LLCamera& camera, LLVector3 &min, LLVector3& max, std::vector<LLVector3>& fp, LLVector3 light_dir = LLVector3(0,0,0));
-	void updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip = 0);  //if water_clip is 0, ignore water plane, 1, cull to above plane, -1, cull to below plane
+	void updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip = 0, LLPlane* plane = NULL);  //if water_clip is 0, ignore water plane, 1, cull to above plane, -1, cull to below plane
 	void createObjects(F32 max_dtime);
 	void createObject(LLViewerObject* vobj);
 	void updateGeom(F32 max_dtime);
@@ -211,6 +218,7 @@ class LLPipeline
 
 	//calculate pixel area of given box from vantage point of given camera
 	static F32 calcPixelArea(LLVector3 center, LLVector3 size, LLCamera& camera);
+	static F32 calcPixelArea(const LLVector4a& center, const LLVector4a& size, LLCamera &camera);
 
 	void stateSort(LLCamera& camera, LLCullResult& result);
 	void stateSort(LLSpatialGroup* group, LLCamera& camera);
@@ -223,6 +231,14 @@ class LLPipeline
 	void renderGroups(LLRenderPass* pass, U32 type, U32 mask, BOOL texture);
 
 	void grabReferences(LLCullResult& result);
+	void clearReferences();
+
+	//check references will assert that there are no references in sCullResult to the provided data
+	void checkReferences(LLFace* face);
+	void checkReferences(LLDrawable* drawable);
+	void checkReferences(LLDrawInfo* draw_info);
+	void checkReferences(LLSpatialGroup* group);
+
 
 	void renderGeom(LLCamera& camera, BOOL forceVBOUpdate = FALSE);
 	void renderGeomDeferred(LLCamera& camera);
@@ -245,6 +261,7 @@ class LLPipeline
 	void generateGI(LLCamera& camera, LLVector3& lightDir, std::vector<LLVector3>& vpc);
 	void renderHighlights();
 	void renderDebug();
+	void renderPhysicsDisplay();
 
 	void rebuildPools(); // Rebuild pools
 
@@ -260,6 +277,7 @@ class LLPipeline
 	void enableLightsStatic();
 	void enableLightsDynamic();
 	void enableLightsAvatar();
+	void enableLightsPreview();
 	void enableLightsAvatarEdit(const LLColor4& color);
 	void enableLightsFullbright(const LLColor4& color);
 	void disableLights();
@@ -303,6 +321,7 @@ class LLPipeline
 	static BOOL toggleRenderTypeControlNegated(void* data);
 	static BOOL toggleRenderDebugControl(void* data);
 	static BOOL toggleRenderDebugFeatureControl(void* data);
+	static void setRenderDebugFeatureControl(U32 bit, bool value);
 
 	static void setRenderParticleBeacons(BOOL val);
 	static void toggleRenderParticleBeacons(void* data);
@@ -337,6 +356,7 @@ class LLPipeline
 	static BOOL getRenderHighlights(void* data);
 
 	static void updateRenderDeferred();
+	static void refreshRenderDeferred();
 
 private:
 	void unloadShaders();
@@ -430,6 +450,9 @@ class LLPipeline
 		RENDER_DEBUG_BUILD_QUEUE		= 0x0200000,
 		RENDER_DEBUG_AGENT_TARGET       = 0x0400000,
 		RENDER_DEBUG_UPDATE_TYPE		= 0x0800000,
+		RENDER_DEBUG_PHYSICS_SHAPES     = 0x1000000,
+		RENDER_DEBUG_NORMALS	        = 0x2000000,
+		RENDER_DEBUG_LOD_INFO	        = 0x4000000,
 	};
 
 public:
@@ -452,6 +475,10 @@ class LLPipeline
 	S32						 mNumVisibleNodes;
 	S32						 mVerticesRelit;
 
+	S32						 mDebugTextureUploadCost;
+	S32						 mDebugSculptUploadCost;
+	S32						 mDebugMeshUploadCost;
+
 	S32						 mLightingChanges;
 	S32						 mGeometryChanges;
 
@@ -467,6 +494,8 @@ class LLPipeline
 	static BOOL				sAutoMaskAlphaNonDeferred;
 	static BOOL				sDisableShaders; // if TRUE, rendering will be done without shaders
 	static BOOL				sRenderBump;
+	static BOOL				sBakeSunlight;
+	static BOOL				sNoAlpha;
 	static BOOL				sUseTriStrips;
 	static BOOL				sUseFarClip;
 	static BOOL				sShadowRender;
@@ -482,7 +511,6 @@ class LLPipeline
 	static BOOL				sRenderAttachedLights;
 	static BOOL				sRenderAttachedParticles;
 	static BOOL				sRenderDeferred;
-	static BOOL             sAllowRebuildPriorityGroup;
 	static S32				sVisibleLightCount;
 	static F32				sMinRenderSize;
 
@@ -501,6 +529,7 @@ class LLPipeline
 	LLRenderTarget			mGIMapPost[2];
 	LLRenderTarget			mLuminanceMap;
 	LLRenderTarget			mHighlight;
+	LLRenderTarget			mPhysicsDisplay;
 
 	//sun shadow map
 	LLRenderTarget			mShadow[6];
@@ -608,6 +637,8 @@ class LLPipeline
 	LLDrawable::drawable_list_t 	mBuildQ2; // non-priority
 	LLSpatialGroup::sg_vector_t		mGroupQ1; //priority
 	LLSpatialGroup::sg_vector_t		mGroupQ2; // non-priority
+	bool mGroupQ2Locked;
+	bool mGroupQ1Locked;
 
 	LLViewerObject::vobj_list_t		mCreateQ;
 		
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index a19eccf748cc52899f6231795473681af057e0c4..d02662681b5d4f6011a18e62b3320ea49de7231f 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -760,6 +760,14 @@
     <color
      name="MenuBarProjectBgColor"
      reference="MdBlue" />
+     
+    <color
+      name="MeshImportTableNormalColor"
+      value="1 1 1 1"/>
+    <color
+      name="MeshImportTableHighlightColor"
+      value="0.2 0.8 1 1"/>
+
   
     <!-- Generic color names (legacy) -->
   <color
diff --git a/indra/newview/skins/default/textures/icons/Inv_Mesh.png b/indra/newview/skins/default/textures/icons/Inv_Mesh.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1f21f7941e59cbcf229ae0900424ad1b1a757df
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Inv_Mesh.png differ
diff --git a/indra/newview/skins/default/textures/icons/check_mark.png b/indra/newview/skins/default/textures/icons/check_mark.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c05297f4f5a15954b5b3fb81742814576bdd662
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/check_mark.png differ
diff --git a/indra/newview/skins/default/textures/model_wizard/divider_line.png b/indra/newview/skins/default/textures/model_wizard/divider_line.png
new file mode 100644
index 0000000000000000000000000000000000000000..76c9e687675f26f2ba50465eaeee21146307515a
Binary files /dev/null and b/indra/newview/skins/default/textures/model_wizard/divider_line.png differ
diff --git a/indra/newview/skins/default/textures/model_wizard/progress_bar_bg.png b/indra/newview/skins/default/textures/model_wizard/progress_bar_bg.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0b213cdc5abfed5d9708437c8a32293cac9d66a
Binary files /dev/null and b/indra/newview/skins/default/textures/model_wizard/progress_bar_bg.png differ
diff --git a/indra/newview/skins/default/textures/model_wizard/progress_light.png b/indra/newview/skins/default/textures/model_wizard/progress_light.png
new file mode 100644
index 0000000000000000000000000000000000000000..019344f812fb6cce7f20bb565c0a008abf6963c8
Binary files /dev/null and b/indra/newview/skins/default/textures/model_wizard/progress_light.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 1ca48b01a89b884167053810e7cc42c14fbe09ea..cc7cce99c991b6ed3d7695df18e1d2c0e8db1e39 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -73,7 +73,22 @@ with the same filename but different name
   <texture name="BackButton_Press" file_name="icons/back_arrow_press.png" preload="false" scale.left="22" scale.top="12" scale.right="25" scale.bottom="12" />
 
   <texture name="Blank" file_name="Blank.png" preload="false" />
-
+	
+  <texture name="BreadCrumbBtn_Left_Disabled" file_name="widgets/BreadCrumbBtn_Left_Disabled.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Left_Off" file_name="widgets/BreadCrumbBtn_Left_Off.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Left_Over" file_name="widgets/BreadCrumbBtn_Left_Over.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Left_Press" file_name="widgets/BreadCrumbBtn_Left_Press.png" preload="false"/>
+ 
+  <texture name="BreadCrumbBtn_Middle_Disabled" file_name="widgets/BreadCrumbBtn_Middle_Disabled.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Middle_Off" file_name="widgets/BreadCrumbBtn_Middle_Off.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Middle_Over" file_name="widgets/BreadCrumbBtn_Middle_Over.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Middle_Press" file_name="widgets/BreadCrumbBtn_Middle_Press.png" preload="false"/>
+
+  <texture name="BreadCrumbBtn_Right_Disabled" file_name="widgets/BreadCrumbBtn_Right_Disabled.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Right_Off" file_name="widgets/BreadCrumbBtn_Right_Off.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Right_Over" file_name="widgets/BreadCrumbBtn_Right_Over.png" preload="false"/>
+  <texture name="BreadCrumbBtn_Right_Press" file_name="widgets/BreadCrumbBtn_Right_Press.png" preload="false"/>
+ 
 
   <texture name="BuyArrow_Over" file_name="navbar/BuyArrow_Over.png" preload="true" scale.left="0" scale.top="1" scale.right="0" scale.bottom="0"  />
   <texture name="BuyArrow_Press" file_name="navbar/BuyArrow_Press.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0"  />
@@ -102,6 +117,7 @@ with the same filename but different name
   <texture name="Checkbox_On" file_name="widgets/Checkbox_On.png" preload="true" />
   <texture name="Checkbox_On_Press" file_name="widgets/Checkbox_On_Press.png" preload="true" />
   <texture name="Checkbox_Press" file_name="widgets/Checkbox_Press.png" preload="true" />
+  <texture name="Check_Mark" file_name="icons/check_mark" preload="true" />
 
   <texture name="ComboButton_Disabled" file_name="widgets/ComboButton_Disabled.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
   <texture name="ComboButton_Selected" file_name="widgets/ComboButton_Selected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
@@ -211,6 +227,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_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" />
   <texture name="Inv_Object_Multi" file_name="icons/Inv_Object_Multi.png" preload="false" />
@@ -258,6 +275,10 @@ with the same filename but different name
 
   <texture name="menu_separator" file_name="navbar/FileMenu_Divider.png" scale.left="4" scale.top="166" scale.right="0" scale.bottom="0" />
 
+  <texture name="ModelImport_Status_Good" file_name="lag_status_good.tga" preload="false"/>
+  <texture name="ModelImport_Status_Warning" file_name="lag_status_warning.tga" preload="false"/>
+  <texture name="ModelImport_Status_Error" file_name="lag_status_critical.tga" preload="false"/>
+  
   <texture name="MouseLook_View_Off" file_name="bottomtray/MouseLook_view_off.png" preload="false" />
   <texture name="MouseLook_View_On" file_name="bottomtray/MouseLook_view_on.png" preload="false" />
 
@@ -626,6 +647,8 @@ with the same filename but different name
   <texture name="icon_for_sale.tga" file_name="icons/Icon_For_Sale.png" />
   <texture name="icon_top_pick.tga" />
 
+  <texture name="inv_folder_mesh.tga"/>
+  <texture name="inv_item_mesh.tga"/>
   <texture name="lag_status_critical.tga" />
   <texture name="lag_status_good.tga" />
   <texture name="lag_status_warning.tga" />
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Disabled.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7c0eaa96bf5f78bbc26e6a913ec6395c55690c6
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Disabled.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Off.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Off.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a73c254fc7dc9aca149802b2058e4c6c43dcf5f
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Off.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Over.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Over.png
new file mode 100644
index 0000000000000000000000000000000000000000..6fb5c432dec5872117c04cb971968d711309f539
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Over.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Press.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Press.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa18517933e9e2800c20f556ab859809f4f7e9c3
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Left_Press.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Disabled.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..bed1a701bdedc5f5b65d47e6ee3e1a5c53b8e0d5
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Disabled.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Off.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Off.png
new file mode 100644
index 0000000000000000000000000000000000000000..57ce9af574f33fda31bdd84a324e3639e90f0705
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Off.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Over.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Over.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c43022f0eaff2d391084842041520e9080727e4
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Over.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Press.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Press.png
new file mode 100644
index 0000000000000000000000000000000000000000..6b8c1baca4c904bb017ce460ee8af6b66c7b99f5
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Middle_Press.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Disabled.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..51505e80c5b40444914494c19118573143456c50
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Disabled.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Off.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Off.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f93efbd93b0d09b98097e62d7f8c92eac2f61af
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Off.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Over.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Over.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a4ec1a315f7d2457fa20fa5cece730738634ced
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Over.png differ
diff --git a/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Press.png b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Press.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f1b4c2ed53268811c3865b56db21c6f8dd9e60f
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/BreadCrumbBtn_Right_Press.png differ
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index 6a1a6006964226bfec43b0ef8292958e7b060bef..a8b3ce9c28fe6ee52a03ab04dd848fa7592f22fe 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -139,27 +139,32 @@ Thank you to the following Residents for helping to ensure that this is the best
        word_wrap="true">
 3Dconnexion SDK Copyright (C) 1992-2007 3Dconnexion
 APR Copyright (C) 2000-2004 The Apache Software Foundation
+Collada DOM Copyright 2005 Sony Computer Entertainment Inc.
 cURL Copyright (C) 1996-2002, Daniel Stenberg, (daniel@haxx.se)
 DBus/dbus-glib Copyright (C) 2002, 2003  CodeFactory AB / Copyright (C) 2003, 2004 Red Hat, Inc.
 expat Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd.
 FreeType Copyright (C) 1996-2002, The FreeType Project (www.freetype.org).
 GL Copyright (C) 1999-2004 Brian Paul.
+GLOD Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia.
 google-perftools Copyright (c) 2005, Google Inc.
 Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
 jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW)
 jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
 ogg/vorbis Copyright (C) 2001, Xiphophorus
 OpenSSL Copyright (C) 1998-2002 The OpenSSL Project.
+PCRE Copyright (c) 1997-2008 University of Cambridge
 SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
 SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
 zlib Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler.
 google-perftools Copyright (c) 2005, Google Inc.
 
+Second Life Viewer uses Havok (TM) Physics. (c)Copyright 1999-2010 Havok.com Inc. (and its Licensors). All Rights Reserved. See www.havok.com for details.
+
 All rights reserved.  See licenses.txt for details.
 
 Voice chat Audio coding: Polycom(R) Siren14(TM) (ITU-T Rec. G.722.1 Annex C)
-      </text_editor>
+        </text_editor>
       </panel>
     </tab_container>
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_import_collada.xml b/indra/newview/skins/default/xui/en/floater_import_collada.xml
new file mode 100644
index 0000000000000000000000000000000000000000..441ab6a2dee648fb348c274d39d4abe46a07c92a
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_import_collada.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater can_close="false" can_drag_on_left="false" can_minimize="false"
+     can_resize="false" height="160" min_height="160" width="300" min_width="300"
+     name="Import Collada" title="Import Scene">
+
+  <text bottom_delta="40" left="20" name="mesh count" height="15" follows="top|left">
+	Meshes: [COUNT]
+  </text>
+
+  <text bottom_delta="20" left="20" name="texture count" height="15" follows="top|left">
+	Textures: [COUNT]
+  </text>
+
+  <text bottom_delta="40" left="10" name="status" height="15" follows="top|left">
+    Status: [STATUS]
+  </text>
+
+   <button
+	  bottom_delta="40"
+      name="cancel"
+      label="Cancel"
+      enabled="true"
+      height="20"
+      layout="topleft"
+      left="125"
+      width="75" />
+
+   <button
+	  bottom_delta="0"
+      name="ok"
+      label="OK"
+      enabled="true"
+      height="20"
+      layout="topleft"
+      left="210"
+      width="75" />
+
+  <string name="status_idle">Idle</string>
+  <string name="status_uploading">Uploading [NAME]</string>
+  <string name="status_creating">Creating object [NAME]</string>
+  
+ </floater>
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 90fee857fbb8e6dcd26a6d505662d50b91a9e0af..c86ed595a7b5ea7ff01673b5ea8b2cf6c2c3e07f 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
@@ -93,6 +93,23 @@
      name="check_landmark"
      top_delta="0"
      width="126" />
+    <icon
+     height="16"
+     image_name="Inv_Mesh"
+     layout="topleft"
+     left="8"
+     mouse_opaque="true"
+     name="icon_mesh"
+     top="142"
+     width="16" />
+    <check_box
+     height="16"
+     label="Meshes"
+     layout="topleft"
+     left_pad="2"
+     name="check_mesh"
+     top_delta="0"
+     width="126" />
     <icon
      height="16"
      image_name="Inv_Notecard"
@@ -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"
@@ -203,7 +220,7 @@
      layout="topleft"
      left="8"
      name="All"
-     top="242"
+     top="262"
      width="100" />
     <button
      follows="left|top"
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d08c3e7078a97e110e813566795457da6b49306e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -0,0 +1,547 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater can_close="true" can_drag_on_left="false" can_minimize="false"
+     can_resize="true" height="550" min_height="550" min_width="620"
+     name="Model Preview" title="Upload Model" width="620">
+
+  <string name="status_idle">Idle</string>
+  <string name="status_reading_file">Loading...</string>
+  <string name="status_generating_meshes">Generating Meshes...</string>
+  <string name="status_vertex_number_overflow">Error: Vertex number is more than 65534, aborted!</string>
+  <string name="high">High</string>
+  <string name="medium">Medium</string>
+  <string name="low">Low</string>
+  <string name="lowest">Lowest</string>
+  <string name="mesh_status_good">Ship it!</string>
+  <string name="mesh_status_na">N/A</string>
+  <string name="mesh_status_none">None</string>
+  <string name="mesh_status_submesh_mismatch">Levels of detail have a different number of textureable faces.</string>
+  <string name="mesh_status_mesh_mismatch">Levels of detail have a different number of mesh instances.</string>
+  <string name="mesh_status_too_many_vertices">Level of detail has too many vertices.</string>
+  <string name="mesh_status_missing_lod">Missing required level of detail.</string>
+  <string name="layer_all">All</string> <!-- Text to display in physics layer combo box for "all layers" -->
+  <string name="decomposing">Analyzing...</string>
+  <string name="simplifying">Simplifying...</string>
+  
+
+  <text left="15" bottom="25" follows="top|left" height="15" name="name_label">
+    Name:
+  </text>
+  <line_editor bottom_delta="20" follows="top|left|right" height="19" 
+	     name="description_form" prevalidate_callback="ascii" width="290" />
+  
+  <text bottom_delta="20" left="15" follows="left|top" height="15" name="lod_label">
+    Preview:
+  </text>
+  <combo_box bottom_delta="20" follows="left|top" height="18"
+	     name="preview_lod_combo" width="240" tool_tip="LOD to view in preview render">
+    <combo_item name="high">
+      Level of Detail: High
+    </combo_item>
+    <combo_item name="medium">
+      Level of Detail: Medium
+    </combo_item>
+    <combo_item name="low">
+      Level of Detail: Low
+    </combo_item>
+    <combo_item name="lowest">
+      Level of Detail: Lowest
+    </combo_item>
+  </combo_box>
+
+    <menu_button follows="top|left" 
+         image_hover_unselected="Toolbar_Left_Over"
+         image_overlay="OptionsMenu_Off"
+         image_selected="Toolbar_Left_Selected"
+         image_unselected="Toolbar_Left_Off"
+         layout="topleft"
+         left_pad="5"
+         name="options_gear_btn"
+         width="31"
+         height="25"/>
+  <!-- Placeholder panel for 3D preview render -->
+  <panel
+    name="preview_panel"
+    left="15"
+    bevel_style="none"
+    border_style="line"
+    border="true"
+    width="290"
+    height="290"
+    follows="all"/>
+
+  <text bottom_delta="25" left="25" width="100" follows="bottom|left">Upload Details</text>
+  <panel top_pad="5" border="true" left="15" width="290" height="70" follows="bottom|left"
+          bevel_style="none" bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3">
+    <text left="25" follows="bottom|left" width="140" height="15" name="streaming cost">
+      Resource Cost: [COST]
+    </text>
+    <text left="25" top_pad="5" width="140" follows="bottom|left" height="15" name="physics cost">
+      Physics Cost: [COST]
+    </text>
+    <text left="25" top_pad="5" follows="bottom|left" height="15" name="upload fee">
+      Upload Fee: N/A
+    </text>
+  </panel>
+
+  <text left="10" bottom="540" width="290" height="15" follows="bottom|left|right" name="status">[STATUS]</text>
+
+  
+  <button bottom="540" left="300"  follows="bottom|right" height="20" label="Defaults"
+	     width="80" name="reset_btn" tool_tip="Reset to defaults"/>
+  <button bottom="540" left="430"  follows="bottom|right" height="20" label="Upload"
+	     width="80" name="ok_btn" tool_tip="Upload to simulator"/>
+  <button left_pad="10" follows="right|bottom" height="20" width="80" label="Cancel" name="cancel_btn"/>
+
+  <tab_container
+    follows="right|top|bottom"
+    top="15"
+    left="310"
+    height="470"
+    width="300"
+    name="import_tab"
+    border="true"
+    tab_position="top">
+
+    <!-- LOD PANEL -->
+    <panel
+      border="true"
+      label="Level of Detail"
+      name="lod_panel">
+
+      <text left="10" width="240" bottom="20" height="15" follows="left|top" name="lod_table_header">
+        Select Level of Detail:
+      </text>
+     
+      <text valign="center" halign="center" bg_visible="true" bottom_delta="16" left="75" width="65" height="18" follows="left|top" value="Triangles"/>
+      <text valign="center" halign="center" bg_visible="true" left_pad="0" width="65" height="18" follows="left|top" value="Vertices"/>
+      <text valign="center" halign="center" left_pad="0" width="65" bg_visible="true" height="18" follows="left|top" value="Status"/>
+      
+      <text valign="center" halign="center" bg_visible="true" name="high_label" left="10" top_pad="0" width="65" height="18" follows="left|top" value="High"/>
+      <text valign="center" halign="center" bg_visible="true" name="high_triangles" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="high_vertices" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="high_status" left_pad="0" width="65" height="18" follows="left|top" value=""/>
+      <icon height="16" width="16" image_name="lag_status_critical.tga" mouse_opaque="true" name="status_icon_high" left_delta="20" top_delta="0" />
+
+      <text valign="center" halign="center" bg_visible="true" name="medium_label" left="10" top_pad="0" width="65" height="18" follows="left|top" value="Medium"/>
+      <text valign="center" halign="center" bg_visible="true" name="medium_triangles" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="medium_vertices" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="medium_status" left_pad="0" width="65" height="18" follows="left|top" value=""/>
+      <icon height="16" width="16" image_name="lag_status_critical.tga" mouse_opaque="true" name="status_icon_medium" left_delta="20" top_delta="0" />
+
+      <text valign="center" halign="center" bg_visible="true" name="low_label" left="10" top_pad="0" width="65" height="18" follows="left|top" value="Low"/>
+      <text valign="center" halign="center" bg_visible="true" name="low_triangles" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="low_vertices" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="low_status" left_pad="0" width="65" height="18" follows="left|top" value=""/>
+      <icon height="16" width="16" image_name="lag_status_critical.tga" mouse_opaque="true" name="status_icon_low" left_delta="20" top_delta="0" />
+
+      <text valign="center" halign="center" bg_visible="true" name="lowest_label" left="10" top_pad="0" width="65" height="18" follows="left|top" value="Lowest"/>
+      <text valign="center" halign="center" bg_visible="true" name="lowest_triangles" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="lowest_vertices" left_pad="0" width="65" height="18" follows="left|top" value="0"/>
+      <text valign="center" halign="center" bg_visible="true" name="lowest_status" left_pad="0" width="65" height="18" follows="left|top" value=""/>
+      <icon height="16" width="16" image_name="lag_status_critical.tga" mouse_opaque="true" name="status_icon_lowest" left_delta="20" top_delta="0" />
+      
+      <text left="10" width="240" height="15" top_pad="15" follows="left|top" name="lod_table_footer">
+        Level of Detail: [DETAIL]
+      </text>
+
+      <icon height="16" width="16" left="20" follows="left|top" name="lod_status_message_icon"/>
+      <text left_pad="5" width="200" height="28" follows="left|top" top_pad="-15" wrap="true" name="lod_status_message_text"/>
+
+      <text top_pad="-3" left="10" height="15" follows="left|top">
+        Mesh
+      </text>
+
+      <radio_group follows="top|left" height="210" left="30" name="lod_file_or_limit" width="240" value="lod_from_file">
+        <radio_item bottom="195" label="Load from file" name="lod_from_file"/>
+        <radio_item bottom="150" label="Auto generate" name="lod_auto_generate"/>
+        <radio_item bottom="0" label="None" name="lod_none"/>
+      </radio_group>
+
+      <line_editor follows="left|top" bottom_delta="-170" width="140" left="45" value="" name="lod_file" height="20"/>
+      <button bottom_delta="3" name="lod_browse" label="Browse..." left_pad="5" follows="left|top" width="70" height="25"/>
+
+      <combo_box follows="top|left" name="lod_mode" top_pad="22" width="100" left="45" height="20">
+        <combo_item name="triangle_limit">
+          Triangle Limit
+        </combo_item>
+        <combo_item name="error_threshold">
+          Error Threshold
+        </combo_item>
+      </combo_box>
+      <spinner follows="top|left" name="lod_triangle_limit" increment="10" left_pad="5" height="20" width="100" decimal_digits="0" enabled="true"/>
+      <spinner left_delta="0" bottom_delta="0" increment="0.01"  follows="top|left" name="lod_error_threshold" min_val="0" max_val="100" height="20" width="100" decimal_digits="3" visible="false" enabled="true"/>
+
+      <text follows="top|left" name="build_operator_text" left="45" top_pad="10" width="100" height="15">
+        Build Operator:  
+      </text>
+      <text follows="top|left" name="queue_mode_text" left_pad="5" width="100" height="15">
+        Queue Mode:
+      </text>
+      <combo_box follows="top|left" name="build_operator" top_pad="5" left="45" width="100" height="20">
+        <combo_item name="edge_collapse">
+          Edge Collapse
+        </combo_item>
+        <combo_item name="half_edge_collapse">
+          Half Edge Collapse
+        </combo_item>
+      </combo_box>
+
+      <combo_box follows="top|left" name="queue_mode" left_pad="5" width="100" height="20">
+        <combo_item name="greedy">
+          Greedy
+        </combo_item>
+        <combo_item name="lazy">
+          Lazy
+        </combo_item>
+        <combo_item name="independent">
+          Independent
+        </combo_item>
+      </combo_box>
+
+      <text top_pad="10" name="border_mode_text" left="45" follows="left|top" width="100" height="15">
+        Border Mode:
+      </text>
+
+      <text left_pad="5" name="share_tolderance_text"  follows="left|top" width="100" height="15">
+        Share Tolerance:
+      </text>
+
+      <combo_box follows="left|top" left="45" height="20" name="border_mode" width="100">
+        <combo_item name="border_unlock">
+          Unlock
+        </combo_item>
+        <combo_item name="border_lock">
+          Lock
+        </combo_item>
+      </combo_box>
+      <spinner follows="left|top" name="share_tolerance" left_pad="5" width="100" decimal_digits="5" initial_value="0.00001" height="20"/>
+             
+      <text left="10" top_pad="35" follows="top|left" width="240" height="15">
+        Generate Normals
+      </text>
+      <text left="35" top_pad="5" follows="top|left" width="100" height="15">
+        Crease Angle:
+      </text>
+      <spinner follows="top|left" left_pad="5" min_val="0" max_val="180" value="75" width="60" height="20" name="crease_angle"/>  
+    </panel>
+
+    <!--  PANEL -->
+    <panel
+      border="true"
+      label="Physics"
+      name="physics_panel">
+
+      <!-- PHYSICS GEOMETRY-->
+      <panel
+        follows="top|left"
+        name="physics geometry"
+        left="0"
+        top="0"
+        width="300"
+        height="65"
+        visible="true"
+        border="true"
+        bevel_style="none" bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3">
+
+        <radio_group follows="top|left" top="10" width="240" height="40" name="physics_load_radio" value="physics_load_from_file">
+          <radio_item bottom="0" name="physics_load_from_file" label="File:"/>
+          <radio_item bottom="23" name="physics_use_lod" label="Use Level of Detail:"/>
+        </radio_group>
+
+        <combo_box left="180" top="10" follows="left|top" height="18"
+	        name="physics_lod_combo" width="110" tool_tip="LOD to use for physics shape">
+          <combo_item name="physics_lowest">
+            Lowest
+          </combo_item>
+          <combo_item name="physics_low">
+            Low
+          </combo_item>
+          <combo_item name="physics_medium">
+            Medium
+          </combo_item>
+          <combo_item name="physics_high">
+            High
+          </combo_item>
+        </combo_box>
+
+        <line_editor follows="left|top" top_pad="5" width="140" left="60" value="" name="physics_file" height="20"/>
+        <button left_pad="10" name="physics_browse" label="Browse..." follows="left|top" width="70" height="20"/>
+
+        <!--
+        <check_box name="physics_optimize" follows="left|top" width="130" left="10" top_pad="5" height="20" label="Optimize"/>
+        <check_box name="physics_use_hull" follows="left|top" width="130" left_pad="5" height="20" label="Use Convex Hull"/>
+        -->
+     </panel>
+
+
+      <!-- PHYSICS ANALYSIS-->
+      <panel
+       follows="top|left"
+       name="physics analysis"
+       top_pad="0"
+       left="0"
+       width="300"
+       height="130"
+       visible="true"
+       border="true"
+       bevel_style="none" bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3">
+
+        <text follows="left|top" bottom="40" height="30" left="10" font="SansSerifBig">
+          Step 1: Analysis
+        </text>
+        
+        <text top_pad="5" width="50" follows="top|left" height="15">
+          Method:
+        </text>
+        <combo_box name="Method" follows="top|left" left_pad="5" bottom_delta="2" height="20" width="80"/>
+        <text left="160" bottom_delta="-2" width="50" follows="top|left" height="15">
+          Quality:
+        </text>
+        <combo_box name="Decompose Quality" bottom_delta="2" follows="top|left" left_pad="5" height="20" width="80"/>
+
+        <slider name="Smooth" left="10" width="280" follows="top|left" top_pad="10" height="20" label="Smooth:"/>
+
+        <check_box name="Close Holes (Slow)" follows="top|left" top_pad="10" height="15" label="Close Holes (slow)"/>
+                
+        <button left="200" bottom_delta="0" width="90" follows="top|left" label="Analyze" name="Decompose" height="20"/>
+        <button left="200" bottom_delta="0" width="90" follows="top|left" label="Cancel" name="decompose_cancel" visble="false" height="20"/>
+      </panel>
+      
+
+      <!-- PHYSICS SIMPLIFICATION -->
+     <panel
+       follows="top|left"
+       name="physics simplification"
+       left="0"
+       top_pad="0"
+       width="300"
+       height="150"
+       visible="true"
+       border="true"
+       bevel_style="none" bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3">
+
+        <text follows="left|top" bottom="40" height="30" left="10" font="SansSerifBig">
+          Step 2: Simplification
+        </text>
+
+        <text left="10" top_pad="5" height="15" width="140" follows="top|left">
+          Method:
+        </text>
+        
+        <combo_box left_pad="5" height="20" width="120" follows="top|left" name="Simplify Method"/>
+
+        <slider left="10" name="Combine Quality" label="Passes:" label_width="120" width="270" follows="top|left" top_pad="10" height="20"/>
+        <slider name="Detail Scale" label="Detail Scale:" label_width="120" width="270" follows="top|left" top_pad="10" height="20"/>
+        <slider name="Retain%" label="Retain:" label_width="120" width="270" follows="top|left" bottom_delta="0" left_delta="0" visible="false" height="20"/>
+        <button left="190" width="90" follows="top|left" label="Simplify" name="Simplify" height="20"/>
+        <button left="190" bottom_delta="0" width="90" follows="top|left" label="Cancel" name="simplify_cancel" height="20"/>
+        
+      </panel>
+
+      <!-- INFO PANEL -->
+      <panel
+        left="0"
+        top_pad="0"
+        width="300"
+        height="100"
+        follows="left|top"
+        name="physics info"
+        visible="true"
+        border="true" 
+        bevel_style="none" bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3">
+
+        <slider name="physics_explode" follows="top|left" top="10" left="10" label="Preview Spread:" min_val="0.0" max_val="3.0" height="20" width="280"/>
+        
+        <text follows="top|left" name="physics_triangles" top_pad="10" height="15" left="10">
+          Triangles: [TRIANGLES]
+        </text>
+        <text follows="top|left" name="physics_points" top_pad="5" height="15">
+          Vertices: [POINTS]
+        </text>
+        <text follows="top|left" name="physics_hulls" top_pad="5" height="15">
+          Hulls: [HULLS]
+        </text>
+
+
+      </panel>
+    </panel>
+
+    <!-- MODIFIERS PANEL -->
+    <panel
+      border="true"
+      label="Modifiers"
+      name="modifiers_panel">
+      <text left="10" width="90" bottom="30" follows="top|left" height="15">
+        Scale:
+      </text>
+      <text left_pad="5" width="140" follows="top|left" height="15">
+        Dimensions:
+      </text>
+
+      <spinner left="10" height="20" follows="top|left" width="80" top_pad="5" value="1.0" min_val="0.01" max_val="64.0" name="import_scale"/>
+
+      <text left_pad="20" height="15" name="import_dimensions" follows="top|left">
+        [X] x [Y] x [Z] m
+      </text>
+
+      <text left="10" top_pad="20" follows="top|left" height="15">
+        Include:
+      </text>
+
+      <check_box top_pad="5" name="upload_textures" height="15" follows="top|left" label="Textures"/>
+      <check_box top_pad="5" name="upload_skin" height="15" follows="top|left" label="Skin weight"/>
+      <check_box top_pad="5" left="20" name="upload_joints" height="15" follows="top|left" label="Joint positions"/>
+
+      <text left="10" top_pad="4" width="90" bottom="30" follows="top|left" height="15">
+        Pelvis Z Offset:
+      </text>
+
+      <spinner left="10" top_pad="4" height="20" follows="top|left" width="80" value="0.0" min_val="-3.00" max_val="3.0" name="pelvis_offset"/>
+
+    </panel>
+  </tab_container>
+  
+  <!--
+  <button bottom_delta="0" left="10" width="120" name="auto fill" label="Generate LOD" tool_tip="Automatically generate levels of detail"/>
+  <button bottom_delta="0" left="140" width="120" name="smooth normals" label="Generate Normals" tool_tip="Regenerate normals based on mesh shape"/>
+  <button bottom_delta="0" left="260" width="120" name="consolidate" label="Consolidate" tool_tip="Combine similar submeshes (reduces number of submeshes)"/>
+  <button bottom_delta="30" left="260" width="120" name="scrub materials" label="Scrub Materials" tool_tip="Remove all material information (clear textures, set all colors to white)."/>
+  
+  <spinner bottom_delta="0" left="140" width="120" height="16" initial_value="75" label_width="60" name="edge threshold" decimal_digits="0" min_val="0" max_val="180" increment="5" label="Hard Angle" tool_tip="Maximum angle that will be smoothed between triangles when using Generate Normals"/>
+
+  <text bottom_delta="30" follows="top|left" height="15" left="10" name="high_lod_label">
+    High LOD:
+  </text>
+  <combo_box bottom_delta="0" left="97" follows="left|top" height="18" 
+             name="high detail combo" width="100" tool_tip="Specify mesh for this level of detail">
+    <combo_item name="high none" value="none">
+      None
+    </combo_item>
+    <combo_item name="high choose file" value="file">
+      Choose File...
+    </combo_item>
+    <combo_item name="high triangle limit" value="limit">
+      Triangle Limit
+    </combo_item>
+  </combo_box>
+  <spinner bottom_delta="-5" left="200" width="120"  name="high limit" decimal_digits="0" increment="1" min_val="0" max_val="100" tool_tip="Triangle budget for this LOD"/>
+  <text bottom_delta="25" follows="top|left" height="15" left="10" name="high info" width="300">
+    [TRIANGLES] Triangles, [VERTICES] Vertices, [SUBMESHES] Submeshes.  
+    [MESSAGE]
+  </text>
+
+  <text bottom_delta="35" follows="top|left" height="15" left="10" name="medium_lod_label">
+    Medium LOD:
+  </text>
+  <combo_box bottom_delta="0" left="97" follows="left|top" height="18"
+             name="medium detail combo" width="100" tool_tip="Specify mesh for this level of detail">
+    <combo_item name="medium none" value="none">
+      None
+    </combo_item>
+    <combo_item name="medium choose file" value="file">
+      Choose File...
+    </combo_item>
+    <combo_item name="medium triangle limit" value="limit">
+      Triangle Limit
+    </combo_item>
+  </combo_box>
+  <spinner bottom_delta="-5" left="200" width="120"  name="medium limit" decimal_digits="0" increment="1" min_val="0" max_val="100" tool_tip="Triangle budget for this LOD"/>
+  <text bottom_delta="25" follows="top|left" height="15" left="10" name="medium info" width="300">
+    [TRIANGLES] Triangles, [VERTICES] Vertices, [SUBMESHES] Submeshes.  
+    [MESSAGE]
+  </text>
+
+  <text bottom_delta="35" follows="top|left" height="15" left="10" name="low_lod_label">
+    Low LOD:
+  </text>
+  <combo_box bottom_delta="0" left="97" follows="left|top" height="18" 
+             name="low detail combo" width="100" tool_tip="Specify mesh for this level of detail">
+    <combo_item name="low none" value="none">
+      None
+    </combo_item>
+    <combo_item name="low choose file" value="file">
+      Choose File...
+    </combo_item>
+    <combo_item name="low triangle limit" value="limit">
+      Triangle Limit
+    </combo_item>
+  </combo_box>
+  <spinner bottom_delta="-5" left="200" width="120"  name="low limit" decimal_digits="0" increment="1" min_val="0" max_val="100" tool_tip="Triangle budget for this LOD"/>
+  <text bottom_delta="25" follows="top|left" height="15" left="10" name="low info" width="300">
+    [TRIANGLES] Triangles, [VERTICES] Vertices, [SUBMESHES] Submeshes
+    [MESSAGE]
+  </text>
+
+  <text bottom_delta="35" follows="top|left" height="15" left="10" name="lowest_lod_label">
+    Lowest LOD:
+  </text>
+  <combo_box bottom_delta="0" left="97" follows="left|top" height="18" 
+             name="lowest detail combo" width="100" tool_tip="Specify mesh for this level of detail">
+    <combo_item name="lowest none" value="none">
+      None
+    </combo_item>
+    <combo_item name="lowest choose file" value="file">
+      Choose File...
+    </combo_item>
+    <combo_item name="lowest triangle limit" value="limit">
+      Triangle Limit
+    </combo_item>
+  </combo_box>
+  <spinner bottom_delta="-5" left="200" width="120"  name="lowest limit" decimal_digits="0" increment="1" min_val="0" max_val="100" tool_tip="Triangle budget for this LOD"/>
+  <text bottom_delta="25" follows="top|left" height="15" left="10" name="lowest info" width="300">
+    [TRIANGLES] Triangles, [VERTICES] Vertices, [SUBMESHES] Submeshes
+    [MESSAGE]
+  </text>
+
+  <text bottom_delta="35" follows="top|left" height="15" left="10" name="physics_lod_label">
+    Physical Shape:
+  </text>
+  <combo_box bottom_delta="0" left="97" follows="left|top" height="18"
+             name="physics detail combo" width="100">
+    <combo_item name="physics none" value="none">
+      None
+    </combo_item>
+    <combo_item name="physics choose file" value="file">
+      Choose File...
+    </combo_item>
+    <combo_item name="physics triangle limit" value="limit">
+      Triangle Limit...
+    </combo_item>
+  </combo_box>
+  <spinner bottom_delta="-5" left="200" width="90"  name="physics limit" decimal_digits="0" increment="1" min_val="0" max_val="100" tool_tip="Triangle budget for this LOD"/>
+  <button bottom_delta="0" left="290" width="30" follows="left|top" height="20" label=">>" 
+          name="decompose_btn" tool_tip="Create convex decomposition."/>
+  <text bottom_delta="25" follows="top|left" height="15" left="10" name="physics info" width="300">
+    [TRIANGLES] Triangles, [HULLS] Hulls, [POINTS] Points
+  </text>
+
+  <text bottom_delta="25" follows="top|left" height="15" left="10" name="include label" width="300">
+    Include:
+  </text>
+
+  <check_box bottom_delta="20" follow="bottom|left" height="20" label="Textures"
+             left="15" width="125" name="upload_textures" tool_tip="Upload associated textures "/>
+
+  <check_box bottom_delta="20" follow="bottom|left" height="20" label="Skin Weights"
+             left="15" width="125" name="upload_skin" tool_tip="Upload vertex skin weighting information."/>
+
+  <check_box bottom_delta="20" follow="bottom|left" height="20" label="Joint Positions"
+             left="15" width="125" name="upload_joints" tool_tip="Upload joint position information (will override avatar joint positions when mesh is worn)."/>
+
+  
+	<button bottom_delta="25" follows="bottom|left" height="20" label="Upload"
+	     left="15" name="ok_btn" width="125" tool_tip="Upload to simulator"/>
+
+  <text bottom_delta="20" left="15" width="280" follows="top|left" height="15" name="description_label" text_color="1 0.82 0.46 1">
+	  (No charge for upload during First Look)
+	</text>
+  <text bottom_delta="20" left="15" width="280" follows="top|left" height="15" name="upload_message">
+    [MESSAGE]
+  </text>
+  
+  <spinner bottom_delta="20" label="Scale" left="15" width="120"  name="debug scale" decimal_digits="3" increment="0.1" min_val="0" max_val="64" initial_value="1" tool_tip="Multiplier for incoming object scale.  If incoming dimensions are very small or very large, modify this value to get dimensions into an acceptable range."/>
+  <text bottom_delta="30" left="15" width="280" follows="top|left" height="15" name="dimensions">
+    Model Dimensions: [X]m x [Y]m x [Z]m
+  </text>
+  -->
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_model_wizard.xml b/indra/newview/skins/default/xui/en/floater_model_wizard.xml
new file mode 100644
index 0000000000000000000000000000000000000000..92d57b20be00a5048bfa1094718c8e259fa10617
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_model_wizard.xml
@@ -0,0 +1,1039 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ layout="topleft"
+ name="Model Wizard"
+ help_topic="model_wizard"
+ bg_opaque_image_overlay="0.5 0.5 0.5 1"
+ height="480"
+ save_rect="true"
+ title="UPLOAD MODEL WIZARD"
+ width="535">
+	<button
+	 top="32"
+	 tab_stop="false"
+	 left="410"
+	 height="32"
+	 name="upload_btn"
+	 enabled="false"
+	 label="5. Upload"
+	 border="false"
+	 image_unselected="BreadCrumbBtn_Right_Off"
+	 image_selected="BreadCrumbBtn_Right_Press"
+	 image_hover_unselected="BreadCrumbBtn_Right_Over"
+	 image_disabled="BreadCrumbBtn_Right_Disabled"
+	 image_disabled_selected="BreadCrumbBtn_Right_Disabled"
+	 width="110">
+		<button.commit_callback
+		function="Wizard.Upload"/>
+	</button>
+	<button
+	 top="32"
+	 left="310"
+	 height="32"
+	 tab_stop="false"
+	 name="review_btn"
+	 label="4. Review"
+	 enabled="false"
+	 border="false"
+	 image_unselected="BreadCrumbBtn_Middle_Off"
+	 image_selected="BreadCrumbBtn_Middle_Press"
+	 image_hover_unselected="BreadCrumbBtn_Middle_Over"
+	 image_disabled="BreadCrumbBtn_Middle_Disabled"
+	 image_disabled_selected="BreadCrumbBtn_Middle_Disabled"
+	 width="110">
+		<button.commit_callback
+		function="Wizard.Review"/>
+	</button>
+	<button
+	 top="32"
+	 left="210"
+	 height="32"
+	 name="physics2_btn"
+	 label="3. Physics"
+	 tab_stop="false"
+	 enabled="false"
+	 border="false"
+	 image_unselected="BreadCrumbBtn_Middle_Off"
+	 image_selected="BreadCrumbBtn_Middle_Press"
+	 image_hover_unselected="BreadCrumbBtn_Middle_Over"
+	 image_disabled="BreadCrumbBtn_Middle_Disabled"
+	 image_disabled_selected="BreadCrumbBtn_Middle_Disabled"
+	 width="110">
+		<button.commit_callback
+		function="Wizard.Physics2"/>
+	</button>
+	<button
+	 top="32"
+	 left="210"
+	 height="32"
+	 name="physics_btn"
+	 label="3. Physics"
+	 tab_stop="false"
+	 enabled="false"
+	 border="false"
+	 image_unselected="BreadCrumbBtn_Middle_Off"
+	 image_selected="BreadCrumbBtn_Middle_Press"
+	 image_hover_unselected="BreadCrumbBtn_Middle_Over"
+	 image_disabled="BreadCrumbBtn_Middle_Disabled"
+	 image_disabled_selected="BreadCrumbBtn_Middle_Disabled"
+	 width="110">
+		<button.commit_callback
+		function="Wizard.Physics"/>
+	</button>
+	<button
+	 top="32"
+	 left="115"
+	 name="optimize_btn"
+	 label="2. Optimize"
+	 tab_stop="false"
+	 height="32"
+	 border="false"
+	 image_unselected="BreadCrumbBtn_Middle_Off"
+	 image_selected="BreadCrumbBtn_Middle_Press"
+	 image_hover_unselected="BreadCrumbBtn_Middle_Over"
+	 image_disabled="BreadCrumbBtn_Middle_Disabled"
+	 image_disabled_selected="BreadCrumbBtn_Middle_Disabled"
+	 width="110">
+		<button.commit_callback
+		function="Wizard.Optimize"/>
+	</button>
+	<button
+	 top="32"
+	 left="15"
+	 name="choose_file_btn"
+	 tab_stop="false"
+	 enabled="false"
+	 label="1. Choose File"
+	 height="32"
+	 image_unselected="BreadCrumbBtn_Left_Off"
+	 image_selected="BreadCrumbBtn_Left_Press"
+	 image_hover_unselected="BreadCrumbBtn_Left_Over"
+	 image_disabled="BreadCrumbBtn_Left_Disabled"
+	 image_disabled_selected="BreadCrumbBtn_Left_Disabled"
+	 width="110">
+		<button.commit_callback
+		function="Wizard.Choose"/>
+	</button>
+	<panel
+		 height="388"
+		 top_pad="0"
+		 name="choose_file_panel"
+		 visible="false"
+		 width="535"
+		 left="0">
+		<panel
+		 height="22"
+		 top_pad="15"
+		 width="505"
+		 name="header_panel"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true"
+		 left="15">
+			<text
+			 width="200"
+			 left="10"
+			 top="3"
+			 name="header_text"
+			 text_color="White"
+			 height="10"
+			 font="SansSerifBig"
+			 layout="topleft">
+				Upload Model
+			</text>
+		</panel>
+		<text
+		 top_pad="14"
+		 width="460"
+		 height="20"
+		 name="description"
+		 font="SansSerifSmall"
+		 layout="topleft"
+		 word_wrap="true"
+		 left_delta="5">
+			This wizard will help you import mesh models to Second Life.  First specify a file containing the model you wish to import.  Second Life supports COLLADA (.dae) files.
+		</text>
+		<panel
+		 top_delta="40"
+		 left="15"
+		 height="270"
+		 width="505"
+		 name="content"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true">
+			<text
+			 type="string"
+			 length="1"
+			 text_color="White" 
+			 follows="left|top"
+			 top="10"
+			 height="10"
+			 layout="topleft"
+			 left_delta="10"
+			 name="Cache location"
+			 width="300">
+				Filename:
+			</text>
+			<line_editor
+			 border_style="line"
+			 border_thickness="1"
+			 follows="left|top"
+			 font="SansSerifSmall"
+			 height="20"
+			 layout="topleft"
+			 left_delta="0"
+			 max_length="4096"
+			 name="lod_file"
+			 top_pad="5"
+			 width="220" />
+			<button
+			 follows="left|top"
+			 height="23"
+			 label="Browse..."
+			 label_selected="Browse..."
+			 layout="topleft"
+			 left_pad="5"
+			 name="browse"
+			 top_delta="-1"
+			 width="85">
+			</button>
+			<text
+			 top_delta="-15"
+			 width="200"
+			 height="15"
+			 font="SansSerifSmall"
+			 layout="topleft"
+			 text_color="White"
+			 left_pad="19">
+				Model Preview:
+			</text>
+			<!-- Placeholder panel for 3D preview render -->
+			<panel
+			 left_delta="0"
+			 top_pad="0"
+			 name="preview_panel"
+			 bevel_style="none"
+			 highlight_light_color="0.09 0.09 0.09 1"
+			 border="true"
+			 height="150"
+			 follows="all"
+			 width="150">
+			</panel>
+			<text
+			 top_pad="10"
+			 width="130"
+			 height="14"
+			 left="340"
+			 text_color="White"
+			 word_wrap="true">
+				Dimensions (meters):
+			</text>
+			<text
+			 top_pad="0"
+			 width="160"
+			 height="15"
+			 font="SansSerifSmallBold" 
+			 text_color="White"
+			 name="dimensions"
+			 left_delta="0">
+				X:         Y:         Z: 
+			</text>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_dividers"
+			 left_delta="41">
+				 |               |   
+			</text>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_x"
+			 left="356"/>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_y"
+			 left="403"/>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_z"
+			 left="450"/>
+			<text
+			 top="100"
+			 width="320"
+			 height="15"
+			 left="10"
+			 text_color="White" 
+			 word_wrap="true">
+				Note:
+			</text>
+			<text
+			 top_pad="0"
+			 width="320"
+			 height="40"
+			 left="10"
+			 word_wrap="true">
+Advanced users familiar with 3d content creation tools may prefer to use the [secondlife:///app/floater/upload_model Advanced Mesh Import Window] .
+			</text>
+		</panel>
+	</panel>
+
+
+	<panel
+		 height="388"
+		 top_delta="0"
+		 name="optimize_panel"
+		 visible="false"
+		 width="535"
+		 left="0">
+		<panel
+		 height="22"
+		 top_pad="15"
+		 name="header_panel"
+		 width="505"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true"
+		 left="15">
+			<text
+			 width="200"
+			 left="10"
+			 name="header_text"
+			 top="3"
+			 text_color="White"
+			 height="10"
+			 font="SansSerifBig"
+			 layout="topleft">
+				Optimize
+			</text>
+		</panel>
+		<text
+		 top_pad="14"
+		 width="460"
+		 height="20"
+		 font="SansSerifSmall"
+		 layout="topleft"
+		 name="description"
+		 word_wrap="true"
+		 left_delta="5">
+			This wizard has optimized your model to improve performance. You may adjust the results of the optimization process bellow or click Next to continue.
+		</text>
+		<panel
+		 top_delta="40"
+		 visible="false"
+		 left="15"
+		 height="270"
+		 width="505"
+		 name="content"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true">
+			<text
+			 top="20"
+			 width="300"
+			 height="12"
+			 font="SansSerifBold"
+			 left="112">Generating Level of Detail</text>
+			<progress_bar
+			  name="optimize_progress_bar"
+              image_fill="model_wizard\progress_light.png"
+			  color_bg="1 1 1 1"
+			  color_bar="1 1 1 0.96"
+			  follows="left|right|top"
+			  width="260"
+			  height="16"
+			  image_bar="model_wizard\progress_bar_bg.png"
+			  top_pad="14"
+			  left="110"/>
+			<icon
+			 top_pad="10"
+			 left_delta="0"
+			 width="13"
+			 height="12"
+			 image_name="model_wizard\check_mark.png"/>
+			<text
+			 top_delta="0"
+			 left_delta="18"
+			 name="high_detail_text"
+			 width="200"
+			 height="14">Generate Level of Detail: High</text>
+			<icon
+			 top_pad="10"
+			 left_delta="-18"
+			 width="13"
+			 height="12"
+			 image_name="model_wizard\check_mark.png"/>
+			<text
+			 top_delta="0"
+			 left_delta="18"
+			 name="medium_detail_text"
+			 width="200"
+			 height="14">Generate Level of Detail: Medium</text>
+			<icon
+			 top_pad="10"
+			 left_delta="-18"
+			 width="13"
+			 height="12"
+			 image_name="model_wizard\check_mark.png"/>
+			<text
+			 top_delta="0"
+			 left_delta="18"
+			 name="low_detail_text"
+			 width="200"
+			 height="14">Generate Level of Detail: Low</text>
+			<icon
+			 top_pad="10"
+			 left_delta="-18"
+			 width="13"
+			 height="12"
+			 image_name="model_wizard\check_mark.png"/>
+			<text
+			 top_delta="0"
+			 left_delta="18"
+			 name="lowest_detail_text"
+			 width="200"
+			 height="14">Generate Level of Detail: Lowest</text>
+		</panel>
+		<panel
+				 top_delta="0"
+				 left_delta="0"
+				 height="270"
+				 width="505"
+				 name="content2"
+				 bg_opaque_color="DkGray2"
+				 background_visible="true"
+				 background_opaque="true">
+			<text top="10" left="10" width="85" text_color="White" follows="left|top" height="15" name="lod_label">
+				Model Preview:
+			</text>
+			<combo_box left_pad="5" top_delta="-5"  follows="left|top" list_position="below" height="22"
+	     name="preview_lod_combo2" width="90" tool_tip="LOD to view in preview render">
+				<combo_item name="high">
+					High
+				</combo_item>
+				<combo_item name="medium">
+					Medium
+				</combo_item>
+				<combo_item name="low">
+					Low
+				</combo_item>
+				<combo_item name="lowest">
+					Lowest
+				</combo_item>
+			</combo_box>
+			<panel
+				 left="10"
+				 top_pad="5"
+				 name="preview_panel"
+				 bevel_style="none"
+				 highlight_light_color="0.09 0.09 0.09 1"
+				 border_style="line"
+				 border="true"
+				 height="185"
+				 follows="all"
+				 width="185">
+			</panel>
+			<text top="45" left="214" text_color="White" font="SansSerifSmallBold" halign="center" width="110" height="30" wrap="true">Higher Performance</text>
+			<text top="75" left="204" halign="center" width="130" word_wrap="true"   font="SansSerifSmall" height="80">Faster rendering but less detailed; lowers Resource (prim) cost.</text>
+			<text top="45" left="378" text_color="White" font="SansSerifSmallBold" halign="center" width="90" height="30" wrap="true">Higher Accuracy</text>
+			<text top="75" left="364" halign="center" width="130" word_wrap="true"   font="SansSerifSmall" height="80">More detailed model but slower; increases Resource (prim) cost.</text>
+
+			<slider
+		   follows="left|top"
+		   height="20"
+		   increment="1"
+		   layout="topleft"
+		   left="204"
+		   max_val="3"
+		   initial_value="2"
+		   min_val="0"
+		   name="accuracy_slider"
+		   show_text="false"
+		   top="130"
+		   width="290" />
+			<text 
+			font="SansSerifSmall" 
+			top_pad="0"  
+			width="300" 
+			left_delta="6" 
+			height="4">'  
+      </text>
+
+
+			<icon
+				 top_pad="14"
+				 left_delta="0"
+				 width="280"
+				 height="2"
+				 image_name="model_wizard\divider_line.png"/>
+	
+			<text top_delta="20" width="200" text_color="White" left_delta="50" name="streaming cost"  height="20">Resource Cost:    [COST]</text>
+			<text
+						 top_pad="15"
+						 width="130"
+						 height="14"
+						 left="10"
+						 text_color="White"
+						 word_wrap="true">
+				Dimensions (meters):
+			</text>
+			<text
+			 top_pad="0"
+			 width="160"
+			 height="15"
+			 font="SansSerifSmallBold"
+			 text_color="White"
+			 name="dimensions"
+			 left_delta="0">
+				X:         Y:         Z:
+			</text>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_dividers"
+			 left_delta="41">
+				|               |
+			</text>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_x"
+			 left_delta="-25"/>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_y"
+			 left_delta="46"/>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_z"
+			 left_delta="46"/>
+		</panel>
+	</panel>
+
+	<panel
+		 height="388"
+		 top_delta="0"
+		 name="physics_panel"
+		 visible="false"
+		 width="535"
+		 left="0">
+		<panel
+		 height="22"
+		 top_pad="15"
+		 name="header_panel"
+		 width="505"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true"
+		 left="15">
+			<text
+			 width="200"
+			 left="10"
+			 name="header_text"
+			 top="3"
+			 height="10"
+			 font="SansSerifBig"
+			 text_color="White" 
+			 layout="topleft">
+				Physics
+			</text>
+		</panel>
+		<text
+		 top_pad="10"
+		 width="474"
+		 height="50"
+		 font="SansSerifSmall"
+		 layout="topleft"
+		 name="description"
+		 word_wrap="true"
+		 left_delta="5">
+			The wizard will create a physical shape, which determines how the object interacts with other objects and avatars. Set the slider to the detail level most appropriate for how your object will be used:
+		</text>
+    <panel
+		 top_delta="44"
+		 left="15"
+		 height="270"
+		 width="505"
+		 name="content"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true">
+      <text top="25" left="30" text_color="White" font="SansSerifSmallBold" width="300" height="4">Performance</text>
+      <text top="45" left="10" halign="center" width="130" word_wrap="true"   font="SansSerifSmall" height="80">Faster rendering but less detailed; lowers Resource (prim) cost.</text>
+      <text top="25" left="390" text_color="White" font="SansSerifSmallBold" width="300" height="4">Accuracy</text>
+      <text top="45" left="360" halign="center" width="130" word_wrap="true"   font="SansSerifSmall" height="80">More detailed model but slower; increases Resource (prim) cost.</text>
+
+      <slider
+		   follows="left|top"
+		   height="22"
+		   increment=".1"
+		   layout="topleft"
+		   left="20"
+		   max_val="1"
+		   initial_value="0.5"
+		   min_val="0"
+		   name="physics_slider"
+		   show_text="false"
+		   top="90"
+		   width="440" />
+      <text
+			font="SansSerifSmall"
+			top_pad="0"
+			width="500"
+			left_delta="6"
+			height="4">'             '             '             '             '              '             '             '             '              '             '</text>
+      <text top_pad="10" width="110" halign="center" word_wrap="true" left="25"  height="40">Recommended for solid objects</text>
+      <text top_delta="0" width="110" halign="center" word_wrap="true" left="190"  height="40">Recommended for buildings</text>
+      <text top_delta="0" width="110" halign="center" word_wrap="true" left="350"  height="40">Recommended for vehicles</text>
+
+
+		<icon
+			 top_pad="5"
+			 left="15"
+			 width="470"
+			 height="2"
+			 image_name="model_wizard\divider_line.png"/>
+		
+	<text top_delta="30" width="180" text_color="White" left="160" name="streaming cost"  height="20">Resource Cost:       [COST]</text>
+ 
+    </panel>
+	</panel>
+
+	<panel
+		 height="388"
+		 top_delta="0"
+		 name="physics2_panel"
+		 visible="true"
+		 width="535"
+		 left="0">
+		<panel
+		 height="22"
+		 top_pad="15"
+		 name="header_panel"
+		 width="505"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true"
+		 left="15">
+			<text
+			 width="200"
+			 left="10"
+			 name="header_text"
+			 text_color="White"
+			 top="3"
+			 height="10"
+			 font="SansSerifBig"
+			 layout="topleft">
+				Physics
+			</text>
+		</panel>
+		<text
+		 top_pad="14"
+		 width="475"
+		 height="50"
+		 font="SansSerifSmall"
+		 layout="topleft"
+		 name="description"
+		 word_wrap="true"
+		 left_delta="5">
+			Preview the physics shape below then click Next to continue.  To modify the physics shape, click the Back button.
+		</text>
+		<panel
+			 top_delta="40"
+			 left="15"
+			 height="270"
+			 width="505"
+			 name="content"
+			 bg_opaque_color="DkGray2"
+			 background_visible="true"
+			 background_opaque="true">
+			<text top="10" left="10" width="85" text_color="White" follows="left|top" height="15" name="lod_label">
+				Model Preview:
+			</text>
+			<combo_box left_pad="5" top_delta="-5"  follows="left|top" list_position="below" height="22"
+			   name="preview_lod_combo3" width="90" tool_tip="LOD to view in preview render">
+				<combo_item name="high">
+					High
+				</combo_item>
+				<combo_item name="medium">
+					Medium
+				</combo_item>
+				<combo_item name="low">
+					Low
+				</combo_item>
+				<combo_item name="lowest">
+					Lowest
+				</combo_item>
+			</combo_box>
+			<panel
+					   left="10"
+					   top_pad="10"
+					   name="preview_panel"
+					   bevel_style="none"
+					   highlight_light_color="0.09 0.09 0.09 1"
+					   border_style="line"
+					   border="true"
+					   height="190"
+					   follows="all"
+					   width="190">
+			</panel>
+			<text
+						 top_pad="8"
+						 width="130"
+						 height="14"
+						 left="10"
+						 text_color="White"
+						 word_wrap="true">
+				Dimensions (meters):
+			</text>
+			<text
+			 top_pad="0"
+			 width="160"
+			 height="15"
+			 font="SansSerifSmallBold"
+			 text_color="White"
+			 name="dimensions"
+			 left_delta="0">
+				X:         Y:         Z:
+			</text>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_dividers"
+			 left_delta="41">
+				|               |
+			</text>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_x"
+			 left_delta="-25"/>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_y"
+			 left_delta="46"/>
+			<text
+			 top_delta="0"
+			 width="160"
+			 height="15"
+			 name="dimension_z"
+			 left_delta="46"/>
+			<text top="60" width="180" text_color="White" left="225" name="streaming cost"  height="20">Resource Cost:       [COST]</text>
+		</panel>
+	</panel>
+
+	<panel
+		 height="388"
+		 top_delta="0"
+		 name="review_panel"
+		 visible="false"
+		 width="535"
+		 left="0">
+		<panel
+		 height="22"
+		 top_pad="15"
+		 name="header_panel"
+		 width="505"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true"
+		 left="15">
+			<text
+			 width="200"
+			 left="10"
+			 name="header_text"
+			 text_color="White" 
+			 top="3"
+			 height="10"
+			 font="SansSerifBig"
+			 layout="topleft">
+				Review
+			</text>
+		</panel>
+		<text
+		 top_pad="14"
+		 width="470"
+		 height="24"
+		 font="SansSerifSmall"
+		 layout="topleft"
+		 name="description"
+		 word_wrap="true"
+		 left_delta="5">
+			Review the details below then click. Upload to upload your model. Your L$ balance will be charged when you click Upload.
+		</text>
+		<icon
+			 top_pad="10"
+			 left="20"
+			 width="495"
+			 height="2"
+			 image_name="model_wizard\divider_line.png"/>
+    <panel
+		 top_pad="5"
+		 left="15"
+		 height="270"
+		 width="505"
+		 name="content">
+      <text top="10" left="10" width="85" text_color="White" follows="left|top" height="15" name="lod_label">
+        Model Preview:
+      </text>
+      <combo_box left_pad="5" top_delta="-5"  follows="left|top" list_position="below" height="22"
+	     name="preview_lod_combo" width="90" tool_tip="LOD to view in preview render">
+        <combo_item name="high">
+          High
+        </combo_item>
+        <combo_item name="medium">
+          Medium
+        </combo_item>
+        <combo_item name="low">
+          Low
+        </combo_item>
+        <combo_item name="lowest">
+          Lowest
+        </combo_item>
+      </combo_box>
+      <panel
+				 left="10"
+				 top_pad="10"
+				 name="preview_panel"
+				 bevel_style="none"
+				 highlight_light_color="0.09 0.09 0.09 1"
+				 border_style="line"
+				 border="true"
+				 height="190"
+				 follows="all"
+				 width="190">
+			</panel>
+		<text
+					 top_pad="8"
+					 width="130"
+					 height="14"
+					 left="10"
+					 text_color="White"
+					 word_wrap="true">
+			Dimensions (meters):
+		</text>
+		<text
+		 top_pad="0"
+		 width="160"
+		 height="15"
+		 font="SansSerifSmallBold"
+		 text_color="White"
+		 name="dimensions"
+		 left_delta="0">
+			X:         Y:         Z:
+		</text>
+		<text
+		 top_delta="0"
+		 width="160"
+		 height="15"
+		 name="dimension_dividers"
+		 left_delta="41">
+			|               |
+		</text>
+		<text
+		 top_delta="0"
+		 width="160"
+		 height="15"
+		 name="dimension_x"
+		 left_delta="-25"/>
+		<text
+		 top_delta="0"
+		 width="160"
+		 height="15"
+		 name="dimension_y"
+		 left_delta="46"/>
+		<text
+		 top_delta="0"
+		 width="160"
+		 height="15"
+		 name="dimension_z"
+		 left_delta="46"/>
+      </panel>
+    <text
+      width="300"
+      height="12"
+      top="125" 
+	  name="streaming cost" 
+      left="230" 
+      font="SansSerifSmallBold" 
+      text_color="White">Resource Cost:         [COST]</text>
+    <text
+      width="285"
+      height="30"
+      top_pad="0"
+      left_delta="0"
+      word_wrap="true"
+      font="SansSerifItalic">This is the cost to your Region's prim/object limit, at default scale</text>
+	<text
+	 width="300"
+	 height="12"
+	 name="physics cost" 
+	 top_pad="10"
+		 left_delta="0"
+	 font="SansSerifSmallBold"
+	 text_color="White">Physics Cost:        [COST]</text>
+	<text
+	  width="285"
+	  height="30"
+	  top_pad="0"
+		  left_delta="0"
+	  word_wrap="true"
+	  font="SansSerifItalic">This is the cost to your Region's prim/object limit, at default scale</text>
+		<text
+		 width="200"
+		 height="12"
+		 top_pad="10"
+		left_delta="0"
+		 font="SansSerifSmallBold"
+		 text_color="White">Upload Fee:</text>
+		<text
+		  width="285"
+		  height="26"
+		  top_pad="0"
+		  left_delta="0"
+		  word_wrap="true"
+		  font="SansSerifItalic">This is the amount the upload will cost.</text>
+		<check_box
+			height="16"
+			layout="topleft"
+			left_delta="0"
+			name="confirm_checkbox"
+			top_pad="15"
+			width="16" />
+		<text
+		  height="100"
+		  width="240"
+		  word_wrap="true" 
+		  left_delta="25"
+		  top_delta="0">I confirm that I have the appropriate rights to the material contained in this model. [secondlife:///app/floater/learn_more Learn more]</text>
+	</panel>
+
+
+
+
+	<panel
+		 height="388"
+		 top_delta="0"
+		 name="upload_panel"
+		 visible="false"
+		 width="535"
+		 left="0">
+		<panel
+		 height="22"
+		 top_pad="15"
+		 name="header_panel"
+		 width="505"
+		 bg_opaque_color="DkGray2"
+		 background_visible="true"
+		 background_opaque="true"
+		 left="15">
+			<text
+			 width="200"
+			 left="10"
+			 name="header_text"
+			 top="3"
+			 text_color="White" 
+			 height="10"
+			 font="SansSerifBig"
+			 layout="topleft">
+				Upload Complete!
+			</text>
+		</panel>
+		<text
+		 top_pad="14"
+		 width="474"
+		 height="20"
+		 font="SansSerifSmall"
+		 layout="topleft"
+		 name="description"
+		 word_wrap="true"
+		 left_delta="5">
+			Congratulations! Your model has been sucessfully uploaded.  You will find the model in the Objects folder in your inventory.
+		</text>
+		<icon
+			 top_pad="15"
+			 left_delta="0"
+			 width="495"
+			 height="2"
+			 image_name="model_wizard\divider_line.png"/>
+	</panel>
+
+
+
+	<button
+	 top="440"
+	 right="-245"
+	 width="90"
+	 height="22"
+	 name="back"
+	 label="&lt;&lt; Back" />
+	<button
+	 top_delta="0"
+	 right="-150"
+	 width="90"
+	 height="22"
+	 name="next"
+	 label="Next &gt;&gt; " />
+	<button
+	 top_delta="0"
+	 right="-150"
+	 width="90"
+	 height="22"
+	 visible="false" 
+	 name="upload" 
+	 tool_tip="Upload to simulator"
+	 label="Upload" />
+	<button
+	 top_delta="0"
+	 right="-15"
+	 width="90"
+	 height="22"
+	 name="cancel"
+	 label="Cancel" />
+	<button
+	 top_delta="0"
+	 right="-15"
+	 width="90"
+	 height="22"
+	 name="close"
+	 visible="false" 
+	 label="Close" />
+	<spinner visible="false" left="10" height="20" follows="top|left" width="80" top_pad="-50" value="1.0" min_val="0.01" max_val="64.0" name="import_scale"/>
+
+	<string name="status_idle">Idle</string>
+	<string name="status_reading_file">Loading...</string>
+	<string name="status_generating_meshes">Generating Meshes...</string>
+  <string name="status_vertex_number_overflow">Error: Vertex number is more than 65534, aborted!</string>
+	<string name="high">High</string>
+	<string name="medium">Medium</string>
+	<string name="low">Low</string>
+	<string name="lowest">Lowest</string>
+	<string name="mesh_status_good">Ship it!</string>
+	<string name="mesh_status_na">N/A</string>
+	<string name="mesh_status_none">None</string>
+	<string name="mesh_status_submesh_mismatch">Levels of detail have a different number of textureable faces.</string>
+	<string name="mesh_status_mesh_mismatch">Levels of detail have a different number of mesh instances.</string>
+	<string name="mesh_status_too_many_vertices">Level of detail has too many vertices.</string>
+	<string name="mesh_status_missing_lod">Missing required level of detail.</string>
+	<string name="layer_all">All</string>
+	<!-- Text to display in physics layer combo box for "all layers" -->
+
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_price_for_listing.xml b/indra/newview/skins/default/xui/en/floater_price_for_listing.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6312366b866f821b45be3458755039f30ac625f1
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_price_for_listing.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_minimize="false"
+ height="240"
+ layout="topleft"
+ name="price_for_listing"
+ help_topic="price_for_listing"
+ title="PUBLISH CLASSIFIED AD"
+ width="320">
+    <text
+     type="string"
+     length="1"
+     bottom="200"
+     follows="top|left"
+     font="SansSerif"
+     height="165"
+     layout="topleft"
+     left="15"
+     word_wrap="true"
+     name="explanation_text">
+        Your classified ad will run for one week from the day it is published.
+
+Your ad&apos;s position in the classified listings is determined by how much you choose to pay.
+
+The highest paid ads go to the top of the list, and appear higher in searches.
+    </text>
+    <text
+     type="string"
+     length="1"
+     follows="top|right"
+     font="SansSerif"
+     height="20"
+     layout="topleft"
+     left="140"
+     name="price_text"
+     top_delta="135"
+     width="85">
+        Price for Ad:
+    </text>
+    <text
+     type="string"
+     length="1"
+     follows="top|right"
+     font="SansSerif"
+     height="20"
+     layout="topleft"
+     left_pad="4"
+     name="price_symbol"
+     top_delta="0"
+     width="20">
+        L$
+    </text>
+    <line_editor
+     follows="top|left"
+     height="20"
+     layout="topleft"
+     left_pad="0"
+     max_length="6"
+     top_delta="-4"
+     name="price_edit"
+     width="60" />
+    <button
+     follows="top|left"
+     height="22"
+     label="OK"
+     layout="topleft"
+     left="105"
+     name="set_price_btn"
+     top_pad="22"
+     width="100" />
+    <button
+     follows="top|left"
+     height="22"
+     label="Cancel"
+     layout="topleft"
+     left_pad="5"
+     name="cancel_btn"
+     width="100" />
+
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index d0603cb30ee19b85953330b38d5beb0abb5907dd..05d47506dbfe9eec55023688bf38781d353f2bfd 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -295,22 +295,24 @@
 	   width="100">
 	   þ: [COUNT]
 	   </text>
-	<check_box
+    <check_box
      control_name="ScaleUniform"
      height="19"
      label=""
      layout="topleft"
      left="143"
      name="checkbox uniform"
-	 top="50"
+     top="50"
      width="20" />
     <text
      height="19"
      label="Stretch Both Sides"
-     left="163"
+     left_delta="20"
      name="checkbox uniform label"
-     top="55"
+     top_delta="2"
      width="120"
+     layout="topleft"
+     follows="top|left"
      wrap="true">
      	Stretch Both Sides
     </text>
@@ -323,6 +325,7 @@
      left="143"
      name="checkbox stretch textures"
      top_pad="-6"
+     follows="left|top"
      width="134" />
    <check_box
      control_name="SnapEnabled"
@@ -361,9 +364,10 @@
      image_selected="ForwardArrow_Press"
      image_unselected="ForwardArrow_Off"
      layout="topleft"
+     follows="top|left"
      name="Options..."
      tool_tip="See more grid options"
-     top_delta="0"
+     top_pad="-22"
      right="-10"
      width="18"
      height="23" >
@@ -745,18 +749,73 @@
 	  <button.commit_callback
 	     function="BuildTool.applyToSelection"/>
     </button>
-    <text
+	<text
+	 text_color="LtGray_50"
+	  type="string"
+	  length="1"
+	  height="10"
+	  follows="left|top"
+	  halign="right"
+	  layout="topleft"
+	  right="-10"
+	  name="obj_count"
+	  top_pad="5"
+	  width="143">
+		Objects: [COUNT]
+	</text>
+	<text
     text_color="LtGray_50"
      type="string"
      length="1"
-     height="10"
+	height="10" 
      follows="left|top"
      halign="right"
      layout="topleft"
      right="-10"
-     name="obj_count"
-     top_pad="7"
+     name="prim_count"
      width="143">
+		Prims: [COUNT]
+	</text>
+    <text
+    text_color="LtGray_50"
+     type="string"
+     length="1"
+     height="10"
+     follows="left|top"
+     halign="right"
+     layout="topleft"
+     right="-120"
+     name="linked_set_count"
+     top="144"
+     width="80">
+        Linked Sets: [COUNT]
+    </text>
+    <text
+    text_color="LtGray_50"
+     type="string"
+     length="1"
+     height="10"
+     follows="left|top"
+     halign="right"
+     layout="topleft"
+     top_delta="0"
+     right="-8"
+     name="linked_set_cost"
+     tool_tip="Cost of currently selected linked sets as [prims],[physics complexity]" 
+     width="80">
+        Cost: [COST] / [PHYSICS]
+    </text>
+    <text
+    text_color="LtGray_50"
+     type="string"
+     length="1"
+     follows="left|top"
+     halign="right"
+     layout="topleft"
+     top_pad="5"
+     right="-120"
+     name="object_count"
+     width="80">
         Objects: [COUNT]
     </text>
     <text
@@ -766,11 +825,39 @@
      follows="left|top"
      halign="right"
      layout="topleft"
-     right="-10"
-     name="prim_count"
-     width="143">
-        Prims: [COUNT]
+	 top_delta="0"
+     right="-8"
+     name="object_cost"
+     tool_tip="Cost of currently selected objects as [prims] / [physics complexity]"
+     width="80">
+        Cost: [COST] / [PHYSICS]
     </text>
+    <!-- <text -->
+    <!-- text_color="LtGray_50" -->
+    <!--  type="string" -->
+    <!--  length="1" -->
+    <!--  height="10" -->
+    <!--  follows="left|top" -->
+    <!--  halign="right" -->
+    <!--  layout="topleft" -->
+    <!--  right="-10" -->
+    <!--  name="obj_count" -->
+    <!--  top_pad="5" -->
+    <!--  width="143"> -->
+    <!--     Objects: [COUNT] -->
+    <!-- </text> -->
+    <!-- <text -->
+    <!-- text_color="LtGray_50" -->
+    <!--  type="string" -->
+    <!--  length="1" -->
+    <!--  follows="left|top" -->
+    <!--  halign="right" -->
+    <!--  layout="topleft" -->
+    <!--  right="-10" -->
+    <!--  name="prim_count" -->
+    <!--  width="143"> -->
+    <!--     Prims: [COUNT] -->
+    <!-- </text> -->
     <tab_container
      follows="left|top"
      height="410"
@@ -783,7 +870,8 @@
      tab_height="25"
      top="173"
      width="295">
-	<panel
+	
+<panel
 	 border="false"
 	 follows="all"
 	 label="General"
@@ -1253,7 +1341,7 @@ even though the user gets a free copy.
       <panel
          border="false"
          follows="all"
-         height="367"
+         height="567"
          label="Object"
          layout="topleft"
          left_delta="0"
@@ -1372,7 +1460,7 @@ even though the user gets a free copy.
              label_width="10"
              layout="topleft"
              left_delta="0"
-             max_val="10"
+             max_val="64"
              min_val="0.01"
              name="Scale X"
              text_enabled_color="1 1 1 1"
@@ -1387,7 +1475,7 @@ even though the user gets a free copy.
              label_width="10"
              layout="topleft"
              left_delta="0"
-             max_val="10"
+             max_val="64"
              min_val="0.01"
              name="Scale Y"
              text_enabled_color="1 1 1 1"
@@ -1402,7 +1490,7 @@ even though the user gets a free copy.
              label_width="10"
              layout="topleft"
              left_delta="0"
-             max_val="10"
+             max_val="64"
              min_val="0.01"
              name="Scale Z"
              text_enabled_color="1 1 1 1"
@@ -1468,7 +1556,6 @@ even though the user gets a free copy.
              text_enabled_color="1 1 1 1"
              top_pad="3"
              width="87" />
-
  <!--           <text
              type="string"
              length="1"
@@ -2046,6 +2133,10 @@ even though the user gets a free copy.
                  label="Cylinder"
                  name="Cylinder"
                  value="Cylinder" />
+              <combo_box.item
+                 label="Mesh"
+                 name="Mesh"
+                 value="Mesh" />
             </combo_box>
         </panel>
         <panel
@@ -2060,6 +2151,9 @@ even though the user gets a free copy.
          name="Features"
          top_delta="0"
          width="295">
+	<panel.string name="None">None</panel.string>
+	<panel.string name="Prim">Prim</panel.string>
+	<panel.string name="Convex Hull">Convex Hull</panel.string>
             <text
              type="string"
              length="1"
@@ -2202,6 +2296,7 @@ even though the user gets a free copy.
              name="FlexForceZ"
              top_pad="4"
              width="128" />
+
             <check_box
              height="16"
              label="Light"
@@ -2312,6 +2407,94 @@ even though the user gets a free copy.
                    mouse_opaque="true"
                    name="Light Ambiance"
                    width="120" />
+            <text
+             type="string"
+             length="1"
+             follows="left|top"
+             height="10"
+             layout="topleft"
+             name="label physicsshapetype"
+             top="38"
+             width="121">
+                Physics Shape Type:
+            </text>
+			<combo_box
+			   height="19"
+			   top_delta="15" 
+			   layout="topleft"
+			   follows="left|top"
+			   name="Physics Shape Type Combo Ctrl"
+			   tool_tip="Choose the physics shape type"
+			   width="108"/>
+
+            <spinner
+             follows="left|top"
+             height="19"
+             increment="1"
+             initial_value="1"
+             label="Gravity"
+             label_width="70"
+             layout="topleft"
+             min_val="-1"
+             max_val="28"
+             name="Physics Gravity"
+             top_pad="10"
+             width="132" />
+
+            <check_box
+             height="19"
+             label="Override material"
+             layout="topleft"
+             left_delta="0"
+             name="Physics Material Override"
+             tool_tip="Override Material"
+             top_pad="10"
+             width="132" />
+
+            <spinner
+             follows="left|top"
+             height="19"
+             increment="0.1"
+             initial_value="0"
+             label="Friction"
+             label_width="70"
+             layout="topleft"
+             left_delta="0"
+             max_val="255"
+             min_val="0"
+             name="Physics Friction"
+             top_pad="4"
+             width="132" />
+
+            <spinner
+             follows="left|top"
+             height="19"
+             increment="0.1"
+             initial_value="0"
+             label="Density"
+             label_width="70"
+             layout="topleft"
+             left_delta="0"
+             max_val="22587"
+             min_val="1"
+             name="Physics Density"
+             top_pad="4"
+             width="132" />
+
+            <spinner
+             follows="left|top"
+             height="19"
+             increment="0.01"
+             initial_value="0"
+             label="Restitution"
+             label_width="70"
+             layout="topleft"
+             left_delta="0"
+             max_val="1"
+             min_val="0"
+             name="Physics Restitution"
+             top_pad="4"
+             width="132" />
         </panel>
          <panel
          border="false"
@@ -2378,7 +2561,7 @@ even though the user gets a free copy.
              initial_value="0"
              layout="topleft"
              left_delta="0"
-             max_val="90"
+             max_val="100"
              name="ColorTrans"
              top_pad="4"
              width="80" />
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
index 90e8db370910d601917395f20a48d98e19e24cce..484af630979539cb0215070ec09042643761591e 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
@@ -41,6 +41,18 @@
                      parameter="" />
                     <menu_item_call.on_enable
                      function="File.EnableUpload" />
+                </menu_item_call>
+		            <menu_item_call
+                 label="Model..."
+                 layout="topleft"
+                 name="Upload Model">
+                <menu_item_call.on_click
+                 function="File.UploadModel"
+                 parameter="" />
+                <menu_item_call.on_enable
+                 function="File.EnableUploadModel" />
+                <menu_item_call.on_visible
+                function="File.VisibleUploadModel"/>
                 </menu_item_call>
                 <menu_item_call
                  label="Bulk (L$[COST] per file)..."
@@ -239,4 +251,4 @@
                      parameter="eyes" />
                 </menu_item_call>
             </menu>
-</menu>
\ No newline at end of file
+</menu>
diff --git a/indra/newview/skins/default/xui/en/menu_model_import_gear_default.xml b/indra/newview/skins/default/xui/en/menu_model_import_gear_default.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2650903f88c687c9aaf29a92a02ae5063c28af4e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_model_import_gear_default.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ bottom="806"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="model_menu_gear_default"
+ visible="false">
+  <menu_item_check
+   label="Show edges"
+   layout="topleft"
+   name="show_edges">
+    <on_click
+       function="ModelImport.ViewOption.Action"
+       parameter="show_edges" />
+    <on_check
+       function="ModelImport.ViewOption.Check"
+       parameter="show_edges" />
+    <on_enable
+			 function="ModelImport.ViewOption.Enabled"
+			 parameter="show_edges" />
+  </menu_item_check>
+  <menu_item_check
+   label="Show physics"
+   layout="topleft"
+   name="show_physics">
+    <on_click
+       function="ModelImport.ViewOption.Action"
+       parameter="show_physics" />
+    <on_check
+       function="ModelImport.ViewOption.Check"
+       parameter="show_physics" />
+    <on_enable
+			 function="ModelImport.ViewOption.Enabled"
+			 parameter="show_physics" />
+  </menu_item_check>
+  <menu_item_check
+  label="Show textures"
+  layout="topleft"
+  name="show_textures">
+    <on_click
+       function="ModelImport.ViewOption.Action"
+       parameter="show_textures" />
+    <on_check
+       function="ModelImport.ViewOption.Check"
+       parameter="show_textures" />
+    <on_enable
+			 function="ModelImport.ViewOption.Enabled"
+			 parameter="show_textures" />
+  </menu_item_check>
+  <menu_item_check
+  label="Show skin weight"
+  layout="topleft"
+  name="show_skin_weight">
+    <on_click
+       function="ModelImport.ViewOption.Action"
+       parameter="show_skin_weight" />
+    <on_check
+       function="ModelImport.ViewOption.Check"
+       parameter="show_skin_weight" />
+    <on_enable
+			 function="ModelImport.ViewOption.Enabled"
+			 parameter="show_skin_weight" />
+  </menu_item_check>
+  <menu_item_check
+  label="Show joint positions"
+  layout="topleft"
+  name="show_joint_positions">
+    <on_click
+       function="ModelImport.ViewOption.Action"
+       parameter="show_joint_positions" />
+    <on_check
+       function="ModelImport.ViewOption.Check"
+       parameter="show_joint_positions" />
+    <on_enable
+			 function="ModelImport.ViewOption.Enabled"
+			 parameter="show_joint_positions" />
+  </menu_item_check>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 8a85a331e5acb36f3d3f09b1fe9dfac6bc7025c7..e8d5c97bbf39b3f323853d8ea18db4084e6a732c 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -89,7 +89,7 @@
          visibility_control="VoiceMorphingEnabled">
             <menu_item_check.on_check
              function="Floater.Visible"
-             parameter="voice_effect" />
+             Parameter="voice_effect" />
             <menu_item_check.on_click
              function="Floater.Toggle"
              parameter="voice_effect" />
@@ -972,6 +972,30 @@
                  parameter="Upload Animation" />
             </menu_item_call>
             <menu_item_call
+           label="Model..."
+           layout="topleft"
+           name="Upload Model">
+            <menu_item_call.on_click
+             function="File.UploadModel"
+             parameter="" />
+            <menu_item_call.on_enable
+             function="File.EnableUploadModel" />
+            <menu_item_call.on_visible
+            function="File.VisibleUploadModel"/>
+            </menu_item_call>
+			<menu_item_call
+           label="Model Wizard..."
+           layout="topleft"
+           name="Upload Model Wizard">
+				<menu_item_call.on_click
+				 function="Floater.Show"
+				 parameter="upload_model_wizard" />
+				<menu_item_call.on_enable
+				 function="File.EnableUploadModel" />
+				<menu_item_call.on_visible
+				function="File.VisibleUploadModel"/>
+			</menu_item_call>
+            <menu_item_call
              label="Bulk (L$[COST] per file)..."
              layout="topleft"
              name="Bulk Upload">
@@ -1342,15 +1366,15 @@
                  parameter="character" />
             </menu_item_check>
             <menu_item_check
-             label="SurfacePath"
-             name="SurfacePath"
+             label="Surface Patch"
+             name="Surface Patch"
              shortcut="control|alt|shift|5">
                 <menu_item_check.on_check
                  function="Advanced.CheckRenderType"
-                 parameter="surfacePath" />
+                 parameter="surfacePatch" />
                 <menu_item_check.on_click
                  function="Advanced.ToggleRenderType"
-                 parameter="surfacePath" />
+                 parameter="surfacePatch" />
             </menu_item_check>
             <menu_item_check
              label="Sky"
@@ -1877,6 +1901,16 @@
                  function="Advanced.ToggleConsole"
                  parameter="memory view" />
             </menu_item_check>
+            <menu_item_check
+               label="Scene Statistics"
+               name="Scene Statistics">
+              <menu_item_check.on_check
+               function="Advanced.CheckConsole"
+               parameter="scene view" />
+              <menu_item_check.on_click
+               function="Advanced.ToggleConsole"
+               parameter="scene view" />
+            </menu_item_check>
 
             <menu_item_separator/>
 
@@ -1961,6 +1995,17 @@
                  function="ToggleControl"
                  parameter="DebugShowTime" />
             </menu_item_check>
+          <menu_item_check
+             label="Show Upload Cost"
+             layout="topleft"
+             name="Show Upload Cost">
+            <menu_item_check.on_check
+             function="CheckControl"
+             parameter="DebugShowUploadCost" />
+            <menu_item_check.on_click
+             function="ToggleControl"
+             parameter="DebugShowUploadCost" />
+          </menu_item_check>
             <menu_item_check
              label="Show Render Info"
              name="Show Render Info">
@@ -2143,6 +2188,16 @@
          function="Advanced.ToggleInfoDisplay"
          parameter="bboxes" />
         </menu_item_check>
+        <menu_item_check
+         label="Normals"
+         name="Normals">
+          <menu_item_check.on_check
+           function="Advanced.CheckInfoDisplay"
+           parameter="normals" />
+          <menu_item_check.on_click
+           function="Advanced.ToggleInfoDisplay"
+           parameter="normals" />
+        </menu_item_check>
         <menu_item_check
          label="Octree"
          name="Octree">
@@ -2163,6 +2218,16 @@
            function="Advanced.ToggleInfoDisplay"
            parameter="shadow frusta" />
         </menu_item_check>
+        <menu_item_check
+         label="Physics Shapes"
+         name="Physics Shapes">
+          <menu_item_check.on_check
+           function="Advanced.CheckInfoDisplay"
+           parameter="physics shapes" />
+          <menu_item_check.on_click
+           function="Advanced.ToggleInfoDisplay"
+           parameter="physics shapes" />
+        </menu_item_check>
         <menu_item_check
          label="Occlusion"
          name="Occlusion">
@@ -2233,6 +2298,26 @@
            function="Advanced.ToggleInfoDisplay"
            parameter="face area" />
         </menu_item_check>
+        <menu_item_check
+         label="LOD Info"
+         name="LOD Info">
+          <menu_item_check.on_check
+           function="Advanced.CheckInfoDisplay"
+           parameter="lod info" />
+          <menu_item_check.on_click
+           function="Advanced.ToggleInfoDisplay"
+           parameter="lod info" />
+        </menu_item_check>
+        <menu_item_check
+         label="Build Queue"
+         name="Build Queue">
+          <menu_item_check.on_check
+           function="Advanced.CheckInfoDisplay"
+           parameter="build queue" />
+          <menu_item_check.on_click
+           function="Advanced.ToggleInfoDisplay"
+           parameter="build queue" />
+        </menu_item_check>
         <menu_item_check
          label="Lights"
          name="Lights">
@@ -2319,19 +2404,6 @@
                 <menu_item_check.on_enable
                  function="Advanced.EnableObjectObjectOcclusion" />
             </menu_item_check>
-          <menu_item_check
-             label="Framebuffer Objects"
-             name="Framebuffer Objects">
-            <menu_item_check.on_check
-             function="CheckControl"
-             parameter="RenderUseFBO" />
-            <menu_item_check.on_click
-             function="ToggleControl"
-             parameter="RenderUseFBO" />
-            <menu_item_check.on_enable
-                 function="Advanced.EnableRenderFBO" />
-           </menu_item_check>
-
           <menu_item_separator />
 
           <menu_item_check
@@ -2758,7 +2830,6 @@
                  function="Floater.Toggle"
                  parameter="region_debug_console" />
             </menu_item_check>
-
             <menu_item_separator />
 
             <menu_item_check
@@ -3166,7 +3237,7 @@
             <menu_item_call.on_click
              function="Advanced.LeaveAdminStatus" />
         </menu_item_call>
-        <menu_item_check
+		<menu_item_check
          label="Show Admin Menu"
          name="View Admin Options">
             <menu_item_check.on_enable
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index d0dd63924942567ebbbd9184615e8f6227e76d2e..b3b9e7f502009e6aac4311256d6a5ddaf1f45b03 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6721,7 +6721,7 @@ Unable to find the help topic for this element.
   <tag>fail</tag>
   </notification>
 
-    <notification
+     <notification
  icon="alertmodal.tga"
  name="ObjectMediaFailure"
  type="alertmodal">
@@ -6753,6 +6753,17 @@ Your voice has been muted by moderator.
          name="okbutton"
          yestext="OK"/>
     </notification>
+  
+   <notification
+    icon="alertmodal.tga"
+    name="UploadCostConfirmation"
+    type="alertmodal">
+This upload will cost L$[PRICE], do you wish to continue with the upload?
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Cancel"
+     yestext="Upload"/>
+  </notification>
 
   <notification
    icon="alertmodal.tga"
@@ -6781,6 +6792,14 @@ The button will be shown when there is enough space for it.
    type="notifytip">
 Select residents to share with.
   </notification>
+
+  <notification
+    name="MeshUploadError"
+    icon="alert.tga"
+    type="alert">
+    [LABEL] failed to upload: [MESSAGE] [IDENTIFIER] [INVALIDITY_IDENTIFIER]
+  </notification>
+   
   <notification
    icon="notifytip.tga"
    name="ShareItemsConfirmation"
@@ -6995,7 +7014,6 @@ Mute everyone?
      notext="Cancel"
      unique="true"/>
   </notification>
-
   <notification
   name="HintChat"
   label="Chat"
@@ -7299,8 +7317,6 @@ The site at &apos;&lt;nolink&gt;[HOST_NAME]&lt;/nolink&gt;&apos; in realm &apos;
    notext="Don't Quit"/>
   </notification>
 
-  <global name="UnsupportedCPU">
-- Your CPU speed does not meet the minimum requirements.
   </global>
 
   <global name="UnsupportedGLRequirements">
diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml
index 7e89860c608a7918b288b8750444a255c2d623a9..774a9e8bbf9a14abe466cc5cb3ee230d9e82b9fc 100644
--- a/indra/newview/skins/default/xui/en/panel_place_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml
@@ -191,7 +191,7 @@
      width="310">
         <panel
          bg_alpha_color="DkGray2"
-         follows="left|top|right"
+         follows="left|top|right|bottom"
          height="580"
          layout="topleft"
          left="0"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
index d74197d965b59833a5437d0923a9c73c990eb4b3..9ecab1a356f9326350556b63f31a75b6802827c5 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
@@ -193,8 +193,19 @@
 		 left_delta="0"
 		 name="BumpShiny"
 		 top_pad="1"
+		width="256" />
+    <check_box
+		control_name="RenderLocalLights"
+		height="16"
+		initial_value="true"
+		label="Local Lights"
+		layout="topleft"
+		left_delta="0"
+		name="LocalLights"
+		 top_pad="1"
 		 width="256" />
-		<check_box
+		width="256" />
+      <check_box
 		 control_name="VertexShaderEnable"
 		 height="16"
 		 initial_value="true"
@@ -221,7 +232,6 @@
 			<check_box.commit_callback
 			 function="Pref.VertexShaderEnable" />
 		</check_box>
-<!-- DISABLED UNTIL WE REALLY WANT TO SUPPORT THIS
     	<check_box
 		 control_name="RenderDeferred"
 		 height="16"
@@ -248,6 +258,19 @@
          	<check_box.commit_callback
 			 function="Pref.VertexShaderEnable" />
     	</check_box>
+      <check_box
+		 control_name="RenderDepthOfField"
+		 height="16"
+		 initial_value="true"
+		 label="Depth of Field"
+		 layout="topleft"
+		 left_delta="0"
+		 name="UseDoF"
+		 top_pad="1"
+		 width="256">
+        <check_box.commit_callback
+     function="Pref.VertexShaderEnable" />
+      </check_box>
 
         <text
          type="string"
@@ -283,7 +306,7 @@
             name="2"
             value="2"/>
         </combo_box>
--->
+
         <text
          type="string"
          length="1"
diff --git a/indra/newview/skins/default/xui/en/panel_region_general.xml b/indra/newview/skins/default/xui/en/panel_region_general.xml
index ca9579284b8e0dc0db16f8be742ad7b06937f433..e0d9f3f71456a996806dedfa1d3345de074d3ba0 100644
--- a/indra/newview/skins/default/xui/en/panel_region_general.xml
+++ b/indra/newview/skins/default/xui/en/panel_region_general.xml
@@ -114,7 +114,7 @@
      layout="topleft"
      left="10"
      name="allow_land_resell_check"
-     top="160"
+     top="150"
      width="80" />
     <check_box
      height="20"
@@ -122,7 +122,7 @@
      layout="topleft"
      left="10"
      name="allow_parcel_changes_check"
-     top="180"
+     top="170"
      width="80" />
     <check_box
      height="20"
@@ -131,7 +131,16 @@
      left="10"
      name="block_parcel_search_check"
      tool_tip="Let people see this region and its parcels in search results"
-     top="200"
+     top="190"
+     width="80" />
+	<check_box
+     height="20"
+     label="Allow Mesh Objects"
+     layout="topleft"
+     left="10"
+     name="mesh_rez_enabled_check"
+     tool_tip="Let people rez mesh objects on this region"
+     top="210"
      width="80" />
     <spinner
      decimal_digits="0"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index b0ede60fa0ee956c2a47f01402bf089c80405ab3..2eb3cd73974e990eb185fe815d294ad93502f18c 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -198,6 +198,7 @@
 	<string name="favorite">favorite</string>
 	<string name="symbolic link">link</string>
 	<string name="symbolic folder link">folder link</string>
+  <string name="mesh">mesh</string>
 
 	<!-- llvoavatar. Displayed in the avatar chat bubble -->
 	<string name="AvatarEditingAppearance">(Editing Appearance)</string>
@@ -2008,6 +2009,7 @@ Requests name of an avatar.  When data is available the dataserver event will be
 	<string name="InvFolder Initial Outfits">Initial Outfits</string>
 	<string name="InvFolder My Outfits">My Outfits</string>
 	<string name="InvFolder Accessories">Accessories</string>
+	<string name="InvFolder Meshes">Meshes</string>
 
   <!-- are used for Friends and Friends/All folders in Inventory "Calling cards" folder. See EXT-694-->
 	<string name="InvFolder Friends">Friends</string>
diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp
index d691bb6c44f6aec08bdb7480729edda9a6234ff0..2ad08dc1f34cf59bfc063639152212ad536d4d90 100644
--- a/indra/newview/tests/llcapabilitylistener_test.cpp
+++ b/indra/newview/tests/llcapabilitylistener_test.cpp
@@ -114,6 +114,7 @@ namespace tut
             regionListener("testCapabilityListener", NULL, provider, LLUUID(), LLUUID()),
             regionPump(regionListener.getCapAPI())
         {
+            LLCurl::initClass();
             provider.setCapability("good", server + "capability-test");
             provider.setCapability("fail", server + "fail");
         }
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 450d274fd7c684155930b9009b7245fb1f939290..8aa94616d67bc7835de696246e538107e9037192 100644
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -263,25 +263,38 @@ def construct(self):
         #self.disable_manifest_check()
 
         self.path(src="../viewer_components/updater/scripts/windows/update_install.bat", dst="update_install.bat")
-
         # Get shared libs from the shared libs staging directory
         if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
                        dst=""):
 
             #self.enable_crt_manifest_check()
-
+            
             # Get llcommon and deps. If missing assume static linkage and continue.
             try:
                 self.path('llcommon.dll')
                 self.path('libapr-1.dll')
                 self.path('libaprutil-1.dll')
                 self.path('libapriconv-1.dll')
+                
             except RuntimeError, err:
                 print err.message
                 print "Skipping llcommon.dll (assuming llcommon was linked statically)"
 
             #self.disable_manifest_check()
 
+            # Mesh 3rd party libs needed for auto LOD and collada reading
+            try:
+                if self.args['configuration'].lower() == 'debug':
+                    self.path("libcollada14dom22-d.dll")
+                else:
+                    self.path("libcollada14dom22.dll")
+                    
+                self.path("glod.dll")
+            except RuntimeError, err:
+                print err.message
+                print "Skipping COLLADA and GLOD libraries (assumming linked statically)"
+
+
             # Get fmod dll, continue if missing
             try:
                 self.path("fmod.dll")
@@ -332,7 +345,7 @@ def construct(self):
         self.path("featuretable_xp.txt")
 
         #self.enable_no_crt_manifest_check()
-        
+
         # Media plugins - QuickTime
         if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"):
             self.path("media_plugin_quicktime.dll")
@@ -646,6 +659,8 @@ def construct(self):
                                     "libaprutil-1.0.dylib",
                                     "libexpat.1.5.2.dylib",
                                     "libexception_handler.dylib",
+                                    "libGLOD.dylib",
+                                    "libcollada14dom.dylib"
                                     ):
                         self.path(os.path.join(libdir, libfile), libfile)
 
@@ -677,6 +692,8 @@ def construct(self):
                                     "libaprutil-1.0.dylib",
                                     "libexpat.1.5.2.dylib",
                                     "libexception_handler.dylib",
+                                    "libGLOD.dylib",
+				    "libcollada14dom.dylib"
                                     ):
                         target_lib = os.path.join('../../..', libfile)
                         self.run_command("ln -sf %(target)r %(link)r" % 
@@ -716,6 +733,7 @@ def construct(self):
             self.run_command('strip -S %(viewer_binary)r' %
                              { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')})
 
+
     def copy_finish(self):
         # Force executable permissions to be set for scripts
         # see CHOP-223 and http://mercurial.selenic.com/bts/issue1802
@@ -946,12 +964,15 @@ def construct(self):
             self.path("libbreakpad_client.so.0.0.0")
             self.path("libbreakpad_client.so.0")
             self.path("libbreakpad_client.so")
+	    self.path("libcollada14dom.so")
             self.path("libdb-5.1.so")
             self.path("libdb-5.so")
             self.path("libdb.so")
             self.path("libcrypto.so.1.0.0")
             self.path("libexpat.so.1.5.2")
             self.path("libssl.so.1.0.0")
+	    self.path("libglod.so")
+	    self.path("libminizip.so")
             self.path("libuuid.so")
             self.path("libuuid.so.16")
             self.path("libuuid.so.16.0.22")
@@ -966,6 +987,9 @@ def construct(self):
             self.path("libopenal.so", "libopenal.so.1")
             self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
             self.path("libfontconfig.so.1.4.4")
+            self.path("libtcmalloc.so", "libtcmalloc.so") #formerly called google perf tools
+            self.path("libtcmalloc.so.0", "libtcmalloc.so.0") #formerly called google perf tools
+            self.path("libtcmalloc.so.0.1.0", "libtcmalloc.so.0.1.0") #formerly called google perf tools
             try:
                     self.path("libfmod-3.75.so")
                     pass
@@ -986,6 +1010,11 @@ def construct(self):
                     self.path("libvivoxplatform.so")
                     self.end_prefix("lib")
 
+            if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():
+                    print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
+                    self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
+
+
 class Linux_x86_64Manifest(LinuxManifest):
     def construct(self):
         super(Linux_x86_64Manifest, self).construct()
diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp
index d450c068ade8b4bc0c481505a8057a379ec83701..c7b70c2de8e91fcd8deec2075c3a66f9df8f8675 100644
--- a/indra/viewer_components/updater/llupdateinstaller.cpp
+++ b/indra/viewer_components/updater/llupdateinstaller.cpp
@@ -28,7 +28,7 @@
 #include "llapr.h"
 #include "llprocesslauncher.h"
 #include "llupdateinstaller.h"
-#include "lldir.h"
+#include "lldir.h" 
 
 
 #if defined(LL_WINDOWS)
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index d292653d3f3a3356f90047628891f0ef30ea922e..2cb0a833d4cfd94a926b7f4477be4b4a20cb8059 100644
--- a/scripts/messages/message_template.msg
+++ b/scripts/messages/message_template.msg
@@ -2061,6 +2061,15 @@ version 2.0
 		{	IsPhantom		BOOL	}
 		{	CastsShadows	BOOL	}
 	}
+    {
+		ExtraPhysics        Variable
+		{   PhysicsShapeType U8     }
+        {   Density          F32    }
+        {   Friction         F32    }
+        {   Restitution      F32    }
+        {   GravityMultiplier    F32    }
+
+	}
 }
 
 
diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1
index 41e70011af9bbd81cdd931893d9e8230b0025b68..6486d92851a7590ef7ba205c7a40390290562e82 100644
--- a/scripts/messages/message_template.msg.sha1
+++ b/scripts/messages/message_template.msg.sha1
@@ -1 +1 @@
-ce3be58f4ea395c272fcfa3e6b6dad027c188e0d
\ No newline at end of file
+465164e1a07f63d68c4ad1f00c19805dfb6ee2d7
\ No newline at end of file