[SCM] osgearth branch, master, updated. debian/1.4.1-1-32-g262f9f2

Pirmin Kalberer pka at sourcepole.ch
Tue Jul 9 20:58:17 UTC 2013


The following commit has been merged in the master branch:
commit d29f0fef97a28199203ade67b207c162df4f3fb3
Author: Pirmin Kalberer <pka at sourcepole.ch>
Date:   Sat Mar 30 20:40:49 2013 +0100

    Upstream release 2.3

diff --git a/src/applications/CMakeLists.txt b/src/applications/CMakeLists.txt
index d12f3ad..f82b456 100644
--- a/src/applications/CMakeLists.txt
+++ b/src/applications/CMakeLists.txt
@@ -23,17 +23,28 @@ SET(TARGET_COMMON_LIBRARIES
 )
 
 SET(TARGET_DEFAULT_PREFIX "application_")
-SET(TARGET_DEFAULT_LABEL_PREFIX "Sample")
+SET(TARGET_DEFAULT_APPLICATION_FOLDER "Applications")
 
+SET(TARGET_DEFAULT_LABEL_PREFIX "Tool")
+SET(TARGET_DEFAULT_APPLICATION_FOLDER "Tools")
 ADD_SUBDIRECTORY(osgearth_viewer)
+ADD_SUBDIRECTORY(osgearth_seed)
+ADD_SUBDIRECTORY(osgearth_featureinfo)
+ADD_SUBDIRECTORY(osgearth_package)
+ADD_SUBDIRECTORY(osgearth_tfs)
+ADD_SUBDIRECTORY(osgearth_boundarygen)
+ADD_SUBDIRECTORY(osgearth_overlayviewer)
+ADD_SUBDIRECTORY(osgearth_version)
+
+
+SET(TARGET_DEFAULT_LABEL_PREFIX "Sample")
+SET(TARGET_DEFAULT_APPLICATION_FOLDER "Samples")
 ADD_SUBDIRECTORY(osgearth_clamp)
 ADD_SUBDIRECTORY(osgearth_manip)
-ADD_SUBDIRECTORY(osgearth_seed)
 ADD_SUBDIRECTORY(osgearth_toc)
 ADD_SUBDIRECTORY(osgearth_elevation)
 ADD_SUBDIRECTORY(osgearth_features)
 ADD_SUBDIRECTORY(osgearth_featurefilter)
-ADD_SUBDIRECTORY(osgearth_featureinfo)
 ADD_SUBDIRECTORY(osgearth_los)
 ADD_SUBDIRECTORY(osgearth_terrainprofile)
 ADD_SUBDIRECTORY(osgearth_map)
@@ -45,20 +56,16 @@ IF(NOT ${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.6")
 ENDIF()
 
 ADD_SUBDIRECTORY(osgearth_measure)
-ADD_SUBDIRECTORY(osgearth_version)
 ADD_SUBDIRECTORY(osgearth_controls)
 ADD_SUBDIRECTORY(osgearth_shadercomp)
 ADD_SUBDIRECTORY(osgearth_tilesource)
 ADD_SUBDIRECTORY(osgearth_imageoverlay)
 ADD_SUBDIRECTORY(osgearth_city)
-ADD_SUBDIRECTORY(osgearth_package)
 ADD_SUBDIRECTORY(osgearth_graticule)
 ADD_SUBDIRECTORY(osgearth_featuremanip)
 ADD_SUBDIRECTORY(osgearth_featurequery)
-ADD_SUBDIRECTORY(osgearth_overlayviewer)
 ADD_SUBDIRECTORY(osgearth_occlusionculling)
 ADD_SUBDIRECTORY(osgearth_colorfilter)
-ADD_SUBDIRECTORY(osgearth_tfs)
 ADD_SUBDIRECTORY(osgearth_contour)
 ADD_SUBDIRECTORY(osgearth_verticalscale)
 
@@ -70,5 +77,3 @@ IF (QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT)
     ADD_SUBDIRECTORY(osgearth_qt)
     ADD_SUBDIRECTORY(osgearth_qt_simple)
 ENDIF()
-
-#ADD_SUBDIRECTORY(osgearth_test_srs)
diff --git a/src/applications/osgearth_boundarygen/BoundaryUtil b/src/applications/osgearth_boundarygen/BoundaryUtil
new file mode 100644
index 0000000..3a66bee
--- /dev/null
+++ b/src/applications/osgearth_boundarygen/BoundaryUtil
@@ -0,0 +1,59 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2012 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+#ifndef BOUNDARY_UTIL
+#define BOUNDARY_UTIL 1
+
+#include <osg/Node>
+
+class BoundaryUtil
+{
+public:
+  /* Use the vertices of the given node to calculate a boundary via the
+   * findHull() method.
+   */
+  static osg::Vec3dArray* getBoundary(osg::Node* modelNode, bool geocentric=true, bool convexHull=false);
+
+  /* Finds the convex hull for the given points using the Andrew's monotone
+   * chain algorithm. Returns an ordered set of points defining the hull
+   */
+  static osg::Vec3dArray* findHull(osg::Vec3dArray& points);
+
+  static osg::Vec3dArray* findMeshBoundary(osg::Node* modelNode, bool geocentric=true);
+
+  static bool simpleBoundaryTest(const osg::Vec3dArray& boundary);
+
+protected:
+  /* Returns an array containing the points sorted first by x and then by y */
+  static osg::Vec3dArray* hullPresortPoints(osg::Vec3dArray& points);
+
+  /* Tests if a point is Left|On|Right of an infinite line
+   *   Returns: >0 for P2 left of the line through P0 and P1
+   *            0 for P2 on the line
+   *            <0 for P2 right of the line
+   *
+   * Implementation based on method from softSurfer (www.softsurfer.com)
+   */
+  static inline float isLeft(osg::Vec3d P0, osg::Vec3d P1, osg::Vec3d P2)
+  {
+    return (P1.x() - P0.x())*(P2.y() - P0.y()) - (P2.x() - P0.x())*(P1.y() - P0.y());
+  }
+};
+
+#endif // BOUNDARY_UTIL
diff --git a/src/applications/osgearth_boundarygen/BoundaryUtil.cpp b/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
new file mode 100644
index 0000000..f49e671
--- /dev/null
+++ b/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
@@ -0,0 +1,519 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2012 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+#include "BoundaryUtil"
+#include "VertexCollectionVisitor"
+#include <algorithm>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/TriangleIndexFunctor>
+#include <osgEarthSymbology/Geometry>
+#include <map>
+#include <vector>
+#include <set>
+
+/* Comparator used to sort osg::Vec3d's first by x and then by y */
+bool presortCompare (osg::Vec3d i, osg::Vec3d j)
+{
+  if (i.x() == j.x())
+    return i.y() < j.y();
+
+  return i.x() < j.x();
+}
+
+/* Use the vertices of the given node to calculate a boundary via the
+ * findHull() method.
+ */
+osg::Vec3dArray* BoundaryUtil::getBoundary(osg::Node* modelNode, bool geocentric, bool convexHull)
+{
+  if (!modelNode)
+    return 0;
+
+  if ( convexHull )
+  {
+    VertexCollectionVisitor v(geocentric);
+    modelNode->accept(v);
+
+    osg::ref_ptr<osg::Vec3dArray> verts = v.getVertices();
+    verts = findHull(*verts);
+
+    osg::EllipsoidModel em;
+    for( osg::Vec3dArray::iterator i = verts->begin(); i != verts->end(); ++i )
+    {
+      em.convertLatLongHeightToXYZ( osg::DegreesToRadians(i->y()), osg::DegreesToRadians(i->x()), i->z(),
+        i->x(), i->y(), i->z() );      
+    }
+
+    return verts.release();
+  }
+  else
+  {
+    return findMeshBoundary( modelNode, geocentric );
+  }
+}
+
+/* Finds the convex hull for the given points using the Andrew's monotone
+ * chian algorithm. Returns an ordered set of points defining the hull
+ *
+ * Implementation based on chainHull_2D() method from 
+ * softSurfer (www.softsurfer.com)
+ */
+osg::Vec3dArray* BoundaryUtil::findHull(osg::Vec3dArray& points)
+{
+  if (points.size() == 0)
+    return 0;
+
+  // the output array hull will be used as the stack
+  osg::Vec3dArray* hull = new osg::Vec3dArray(points.size());
+
+  // Presort the points as required by the algorithm
+  osg::ref_ptr<osg::Vec3dArray> sorted = hullPresortPoints(points);
+  
+  int bot=0, top=(-1);  // indices for bottom and top of the stack
+  int i;                // array scan index
+  int n = sorted->size();
+
+  // Get the indices of points with min x-coord and min|max y-coord
+  int minmin = 0, minmax;
+  double xmin = (*sorted)[0].x();
+  for (i=1; i<n; i++)
+    if ((*sorted)[i].x() != xmin) break;
+  minmax = i-1;
+
+  //if the points at minmin and minmax have the same value, shift minmin
+  //to ignore the duplicate points
+  if ((*sorted)[minmin] == (*sorted)[minmax])
+    minmin = minmax;
+
+  // degenerate case: all x-coords == xmin
+  if (minmax == n-1)
+  {       
+    (*hull)[++top] = (*sorted)[minmin];
+    if ((*sorted)[minmax].y() != (*sorted)[minmin].y()) // a nontrivial segment
+      (*hull)[++top] = (*sorted)[minmax];
+    (*hull)[++top] = (*sorted)[minmin];           // add polygon endpoint
+
+    hull->resize(top + 1);
+    return hull;
+  }
+
+  // Get the indices of points with max x-coord and min|max y-coord
+  int maxmin, maxmax = n-1;
+  double xmax = (*sorted)[n-1].x();
+  for (i=n-2; i>=0; i--)
+    if ((*sorted)[i].x() != xmax) break;
+  maxmin = i+1;
+
+  // Compute the lower hull on the stack
+  (*hull)[++top] = (*sorted)[minmin];  // push minmin point onto stack
+  i = minmax;
+  while (++i <= maxmin)
+  {
+    // the lower line joins (*sorted)[minmin] with (*sorted)[maxmin]
+
+    // if multiple points have the same x/y values, go with the lowest z value
+    if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*sorted)[i].y())
+    {
+      if ((*sorted)[i].z() < (*hull)[top].z())
+        (*hull)[top].z() = (*sorted)[i].z();
+
+      continue;
+    }
+
+    if (isLeft((*sorted)[minmin], (*sorted)[maxmin], (*sorted)[i]) > 0 && i < maxmin)
+      continue;  // ignore (*sorted)[i] above the lower line.  NOTE: Differs from original CH algorithm in that it keeps collinear points
+
+    while (top > 0)  // there are at least 2 points on the stack
+    {
+      // test if (*sorted)[i] is left of or on the line at the stack top. NOTE: Differs from original CH algorithm in that it keeps collinear points
+      if (isLeft((*hull)[top-1], (*hull)[top], (*sorted)[i]) >= 0)
+        break;  // (*sorted)[i] is a new hull vertex
+      else
+        top--;  // pop top point off stack
+    }
+    (*hull)[++top] = (*sorted)[i];  // push (*sorted)[i] onto stack
+  }
+
+  if (maxmax != maxmin)  // if distinct xmax points
+  {
+    // Push all points between maxmin and maxmax onto stack. NOTE: Differs from original CH algorithm in that it keeps collinear points
+    while (i <= maxmax)
+    {
+      if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*hull)[top].y())
+      {
+        if ((*sorted)[i].z() < (*hull)[top].z())
+          (*hull)[top].z() = (*sorted)[i].z();
+      }
+      else
+      {
+        (*hull)[++top] = (*sorted)[i];
+      }
+
+      i++;
+    }
+  }
+
+  // Next, compute the upper hull on the stack H above the bottom hull
+
+  bot = top;  // the bottom point of the upper hull stack
+  i = maxmin;
+  while (--i >= minmax)
+  {
+    // the upper line joins (*sorted)[maxmax] with (*sorted)[minmax]
+
+    // if multiple points have the same x/y values, go with the lowest z value
+    if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*sorted)[i].y())
+    {
+      if ((*sorted)[i].z() < (*hull)[top].z())
+        (*hull)[top].z() = (*sorted)[i].z();
+
+      continue;
+    }
+
+    if (isLeft((*sorted)[maxmax], (*sorted)[minmax], (*sorted)[i]) > 0 && i > minmax)
+      continue;  // ignore (*sorted)[i] below the upper line. NOTE: Differs from original CH algorithm in that it keeps collinear points
+
+    while (top > bot)  // at least 2 points on the upper stack
+    {
+      // test if (*sorted)[i] is left of or on the line at the stack top. NOTE: Differs from original CH algorithm in that it keeps collinear points
+      if (isLeft((*hull)[top-1], (*hull)[top], (*sorted)[i]) >= 0)
+        break;   // (*sorted)[i] is a new hull vertex
+      else
+        top--;   // pop top point off stack
+    }
+    (*hull)[++top] = (*sorted)[i];  // push (*sorted)[i] onto stack
+  }
+
+  // If minmax and minmin are the same, remove the duplicate point
+  if (minmax == minmin)
+  {
+    top--;
+  }
+  else
+  {
+    // Push all points between minmax and minmin onto stack. NOTE: Differs from original CH algorithm in that it keeps collinear points
+    while (i > minmin)
+    {
+      if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*hull)[top].y())
+      {
+        if ((*sorted)[i].z() < (*hull)[top].z())
+          (*hull)[top].z() = (*sorted)[i].z();
+      }
+      else
+      {
+        (*hull)[++top] = (*sorted)[i];
+      }
+
+      i--;
+    }
+  }
+
+  hull->resize(top + 1);
+  return hull;
+}
+
+/* Returns an array containing the points sorted first by x and then by y */
+osg::Vec3dArray* BoundaryUtil::hullPresortPoints(osg::Vec3dArray& points)
+{
+  osg::Vec3dArray* sorted = new osg::Vec3dArray(points.begin(), points.end());
+  std::sort(sorted->begin(), sorted->end(), presortCompare);
+
+  return sorted;
+}
+
+//---------------------------------------------------------------------------
+
+namespace
+{
+    typedef std::set<osg::Vec3d> VertexSet; 
+    typedef VertexSet::iterator  Index;
+
+    // custom comparator for Index so we can use it at a std::map key
+    struct IndexLess : public std::less<Index> {
+      bool operator()(const Index& lhs, const Index& rhs ) const {
+        return (*lhs) < (*rhs);
+      }
+    };
+    
+    typedef std::set<Index, IndexLess>            IndexSet;
+    typedef std::map<Index, IndexSet, IndexLess>  EdgeMap;
+
+    // Stores the noded topology of a model with unique verticies and edge definitions.
+    // The verticies are stored rotated into the XY plane so that we can properly find
+    // the "bottom-most" vert and walk the boundary.
+    struct TopologyGraph
+    {
+        TopologyGraph()
+          : _minY( _verts.end() ) { }
+
+        VertexSet _verts;    // set of unique verts in the topology (rotated into XY plane)
+        osg::Quat _rot;      // rotates a geocentric vert into the XY plane
+        EdgeMap   _edgeMap;  // maps each vert to all the verts with which it shares an edge
+        Index     _minY;     // points to the vert with the minimum Y coordinate (in XY plane)
+    };
+
+    typedef std::map<unsigned,Index> UniqueMap;
+
+    // A TriangleIndexFunctor that traverses a stream of triangles and builds a
+    // topology graph from their points and edges.
+    struct TopologyBuilder
+    {
+        TopologyGraph*  _topology;     // topology to which to append point and edge data
+        osg::Vec3Array* _vertexList;   // source vertex list
+        osg::Matrixd    _local2world;  // transforms source verts into world coordinates
+        UniqueMap       _uniqueMap;    // prevents duplicates
+
+        void operator()( unsigned v0, unsigned v1, unsigned v2 )
+        {
+            Index i0 = add( v0 );
+            Index i1 = add( v1 );
+            Index i2 = add( v2 );
+
+            // add to the edge list for each of these verts
+            _topology->_edgeMap[i0].insert( i1 );
+            _topology->_edgeMap[i0].insert( i2 );
+            _topology->_edgeMap[i1].insert( i0 );
+            _topology->_edgeMap[i1].insert( i2 );
+            _topology->_edgeMap[i2].insert( i0 );
+            _topology->_edgeMap[i2].insert( i1 );
+        }
+
+        Index add( unsigned v )
+        {
+            // first see if we already added this vert:
+            UniqueMap::iterator i = _uniqueMap.find( v );
+            if ( i == _uniqueMap.end() )
+            {
+              // no, so transform it into world coordinates, and rotate it into the XY plane
+              osg::Vec3d spVert = (*_vertexList)[v];
+              osg::Vec3d local = _topology->_rot * (spVert * _local2world);
+
+              // insert it into the unique vert list
+              std::pair<VertexSet::iterator,bool> f = _topology->_verts.insert( local );
+              if ( f.second )
+              {
+                // this is a new location, so check it to see if it is the new "lowest" point:
+                if ( _topology->_minY == _topology->_verts.end() || local.y() < _topology->_minY->y() )
+                  _topology->_minY = f.first;
+
+                // store in the uniqueness map to prevent duplication
+                _uniqueMap[ v ] = f.first;
+              }
+             
+              // return the index of the vert.
+              return f.first;
+            }
+            else
+            {
+              // return the index of the vert.
+              return i->second;
+            }
+        }
+    };
+
+    // Visits a scene graph and builds a topology graph from the verts and edges
+    // found within.
+    struct BuildTopologyVisitor : public osg::NodeVisitor
+    {
+        BuildTopologyVisitor( TopologyGraph& topology )
+            : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
+              _topology( topology )
+        {
+            //nop
+        }
+
+        // track local transforms so we can build a topology in world coords
+        void apply( osg::Transform& xform )
+        {
+            osg::Matrix matrix;
+            if ( !_matrixStack.empty() ) matrix = _matrixStack.back();
+            xform.computeLocalToWorldMatrix( matrix, this );
+            _matrixStack.push_back( matrix );
+            traverse( xform );
+            _matrixStack.pop_back();
+        }
+
+        // add the contents of a geode to the topology
+        void apply( osg::Geode& geode )
+        {
+            for( unsigned i=0; i<geode.getNumDrawables(); ++i )
+            {
+                osg::Drawable* drawable = geode.getDrawable( i );
+                if ( drawable->asGeometry() )
+                {
+                    apply( drawable->asGeometry() );
+                }
+            }
+        }
+
+        // add the contents of a Geometry to the topology
+        void apply( osg::Geometry* geometry )
+        {
+            osg::Vec3Array* vertexList = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
+            if ( vertexList )
+            {
+                osg::TriangleIndexFunctor<TopologyBuilder> _builder;
+                _builder._topology = &_topology;
+                _builder._vertexList = vertexList;
+                if ( !_matrixStack.empty() )
+                  _builder._local2world = _matrixStack.back();
+                geometry->accept( _builder );
+            }
+        }
+
+        std::vector<osg::Matrixd> _matrixStack;
+        TopologyGraph&            _topology;
+    };
+}
+
+//------------------------------------------------------------------------
+
+osg::Vec3dArray*
+BoundaryUtil::findMeshBoundary( osg::Node* node, bool geocentric )
+{
+    // the normal defines the XY plane in which to search for a boundary
+    osg::Vec3d normal(0,0,1);
+
+    if ( geocentric )
+    {
+        // define the XY plane based on the normal to the center of the dataset:
+        osg::BoundingSphere bs = node->getBound();
+        normal = bs.center();
+        normal.normalize();
+    }
+
+    osg::ref_ptr<osg::Vec3dArray> _result = new osg::Vec3dArray();
+
+    // first build a topology graph from the node.
+    TopologyGraph topology;
+
+    // set up a quat that will rotate geometry into our XY plane
+    if ( normal != osg::Vec3(0,0,1) )
+        topology._rot.makeRotate( normal, osg::Vec3d(0,0,1) );
+
+    // build the topology
+    BuildTopologyVisitor buildTopoVisitor(topology);
+    node->accept( buildTopoVisitor );
+
+    // starting with the minimum-Y vertex (which is guaranteed to be in the boundary)
+    // traverse the outside of the point set. Do this by sorting all the edges by
+    // their angle relative to the vector to the previous point. The vector with the
+    // smallest angle represents the edge connecting the current point to the next
+    // boundary point. Walk the edge until we return to the beginning.
+    
+    Index vptr      = topology._minY;
+    Index vptr_prev = topology._verts.end();
+
+    while( true )
+    {
+        // store this vertex in the result set:
+        _result->push_back( *vptr );
+
+        // pull up the next 2D vertex (XY plane):
+        osg::Vec2d vert( vptr->x(), vptr->y() );
+
+        // construct the "base" vector that points from the current point back
+        // to the previous point; or to -X in the initial case
+        osg::Vec2d base;
+        if ( vptr_prev == topology._verts.end() )
+            base.set( -1, 0 );
+        else
+            base = osg::Vec2d( vptr_prev->x(), vptr_prev->y() ) - vert;
+            
+        // pull up the edge set for this vertex:
+        IndexSet& edges = topology._edgeMap[vptr];
+
+        // find the edge with the minimun delta angle to the base vector
+        double minAngle = DBL_MAX;
+        Index  minEdge  = topology._verts.end();
+
+        for( IndexSet::iterator e = edges.begin(); e != edges.end(); ++e )
+        {
+            // don't go back from whence we just came
+            if ( *e == vptr_prev )
+              continue;
+
+            // calculate the angle between the base vector and the current edge:
+            osg::Vec2d edgeVert( (*e)->x(), (*e)->y() );
+            osg::Vec2d edge = edgeVert - vert;
+
+            double baseAngle = atan2(base.y(), base.x());
+            double edgeAngle = atan2(edge.y(), edge.x());
+
+            double outsideAngle = baseAngle - edgeAngle;
+
+            // normalize it to [0..360)
+            if ( outsideAngle < 0.0 )
+              outsideAngle += 2.0*osg::PI;
+
+            // see it is qualifies as the new minimum angle
+            if ( outsideAngle < minAngle )
+            {
+                minAngle = outsideAngle;
+                minEdge = *e;
+            }
+        }
+
+        if ( minEdge == topology._verts.end() )
+        {
+            // this will probably never happen
+            osg::notify(osg::WARN) << "Illegal state - bailing" << std::endl;
+            return 0L;
+        }
+
+        vptr_prev = vptr;
+
+        // follow the chosen edge around the outside of the geometry:
+        vptr = minEdge;
+
+        // once we make it all the way around, we're done:
+        if ( vptr == topology._minY )
+            break;
+    }
+
+    // un-rotate the results from the XY plane back to their original frame:
+    osg::Quat invRot = topology._rot.inverse();
+    for( osg::Vec3dArray::iterator i = _result->begin(); i != _result->end(); ++i )
+    {
+      (*i) = invRot * (*i);
+    }
+
+    return _result.release();
+}
+
+//------------------------------------------------------------------------
+
+bool
+BoundaryUtil::simpleBoundaryTest(const osg::Vec3dArray& boundary)
+{
+  osg::ref_ptr<osgEarth::Symbology::Polygon> boundsPoly = new osgEarth::Symbology::Polygon();
+  for (int i=0; i < (int)boundary.size(); i++)
+    boundsPoly->push_back(boundary[i]);
+
+  osgEarth::Bounds boundsBounds = boundsPoly->getBounds();
+
+  osg::ref_ptr<osgEarth::Symbology::Polygon> outterPoly = new osgEarth::Symbology::Polygon();
+  outterPoly->push_back(osg::Vec3d(boundsBounds.xMin() - 10.0, boundsBounds.yMin() - 10.0, boundsBounds.zMin()));
+  outterPoly->push_back(osg::Vec3d(boundsBounds.xMax() + 10.0, boundsBounds.yMin() - 10.0, boundsBounds.zMin()));
+  outterPoly->push_back(osg::Vec3d(boundsBounds.xMax() + 10.0, boundsBounds.yMax() + 10.0, boundsBounds.zMin()));
+  outterPoly->push_back(osg::Vec3d(boundsBounds.xMin() - 10.0, boundsBounds.yMax() + 10.0, boundsBounds.zMin()));
+
+  osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly;
+  return outterPoly->difference(boundsPoly, outPoly);
+}
diff --git a/src/applications/osgearth_boundarygen/CMakeLists.txt b/src/applications/osgearth_boundarygen/CMakeLists.txt
new file mode 100644
index 0000000..fe9ec6b
--- /dev/null
+++ b/src/applications/osgearth_boundarygen/CMakeLists.txt
@@ -0,0 +1,16 @@
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
+SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
+
+SET(TARGET_H
+    BoundaryUtil
+    VertexCollectionVisitor
+)
+
+SET(TARGET_SRC
+    BoundaryUtil.cpp
+    VertexCollectionVisitor.cpp
+    boundarygen.cpp
+)
+
+#### end var setup  ###
+SETUP_APPLICATION(osgearth_boundarygen)
diff --git a/src/applications/osgearth_boundarygen/VertexCollectionVisitor b/src/applications/osgearth_boundarygen/VertexCollectionVisitor
new file mode 100644
index 0000000..7f7fcc3
--- /dev/null
+++ b/src/applications/osgearth_boundarygen/VertexCollectionVisitor
@@ -0,0 +1,60 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2012 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+#ifndef VERTEX_COLLECTION_VISITOR
+#define VERTEX_COLLECTION_VISITOR 1
+
+#include <osg/CoordinateSystemNode>
+#include <osg/NodeVisitor>
+
+class VertexCollectionVisitor : public osg::NodeVisitor
+{
+public:
+
+    VertexCollectionVisitor(bool geocentric = false, TraversalMode traversalMode = TRAVERSE_ALL_CHILDREN);
+    
+    virtual void reset();
+    
+    osg::Vec3dArray* getVertices() { return _vertices.get(); }
+    
+    void apply(osg::Node& node);
+    
+    void apply(osg::Transform& transform);
+    
+    void apply(osg::Geode& geode);
+    
+    inline void pushMatrix(osg::Matrix& matrix) { _matrixStack.push_back(matrix); }
+    
+    inline void popMatrix() { _matrixStack.pop_back(); }
+
+    void applyDrawable(osg::Drawable* drawable);
+    
+protected:
+    
+    typedef std::vector<osg::Matrix> MatrixStack;
+
+    osg::ref_ptr<osg::Vec3dArray>  _vertices;
+    MatrixStack _matrixStack;
+    bool _geocentric;
+    osg::ref_ptr<osg::EllipsoidModel> _ellipsoidModel;
+
+    void addVertex(osg::Vec3 vertex);
+};
+
+#endif //VERTEX_COLLECTION_VISITOR
diff --git a/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp b/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp
new file mode 100644
index 0000000..138c701
--- /dev/null
+++ b/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp
@@ -0,0 +1,100 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2012 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+#include "VertexCollectionVisitor"
+#include <osg/CoordinateSystemNode>
+#include <osg/Drawable>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Transform>
+
+VertexCollectionVisitor::VertexCollectionVisitor(bool geocentric, TraversalMode traversalMode):
+  _geocentric(geocentric), osg::NodeVisitor(traversalMode)
+{
+  _vertices = new osg::Vec3dArray();
+  _ellipsoidModel = new osg::EllipsoidModel();
+}
+
+void VertexCollectionVisitor::reset()
+{
+    _matrixStack.clear();
+    _vertices->clear();
+}
+
+void VertexCollectionVisitor::apply(osg::Node& node)
+{
+    traverse(node);
+}
+
+void VertexCollectionVisitor::apply(osg::Transform& transform)
+{
+    osg::Matrix matrix;
+    if (!_matrixStack.empty()) matrix = _matrixStack.back();
+
+    transform.computeLocalToWorldMatrix(matrix,this);
+
+    pushMatrix(matrix);
+
+    traverse(transform);
+
+    popMatrix();
+}
+
+void VertexCollectionVisitor::apply(osg::Geode& geode)
+{
+  for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
+    applyDrawable(geode.getDrawable(i));
+}
+
+void VertexCollectionVisitor::applyDrawable(osg::Drawable* drawable)
+{
+  osg::Geometry* geometry = drawable->asGeometry();
+  if (geometry)
+  {
+    osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
+    if (verts)
+    {
+      if (_matrixStack.empty())
+      {
+        for (osg::Vec3Array::iterator it=verts->begin(); it != verts->end(); ++it)
+          addVertex(*it);
+      }
+      else
+      {
+        osg::Matrix& matrix = _matrixStack.back();
+        for (osg::Vec3Array::iterator it=verts->begin(); it != verts->end(); ++it)
+          addVertex((*it) * matrix);
+      }
+    }
+  }
+}
+
+void VertexCollectionVisitor::addVertex(osg::Vec3 vertex)
+{
+  if (_geocentric)
+  {
+    double lat, lon, height;
+    _ellipsoidModel->convertXYZToLatLongHeight(vertex.x(), vertex.y(), vertex.z(), lat, lon, height);
+    _vertices->push_back(osg::Vec3d(osg::RadiansToDegrees(lon), osg::RadiansToDegrees(lat), height));
+  }
+  else
+  {
+    _vertices->push_back(vertex);
+  }
+}
diff --git a/src/applications/osgearth_boundarygen/boundarygen.cpp b/src/applications/osgearth_boundarygen/boundarygen.cpp
new file mode 100644
index 0000000..3c4dbd9
--- /dev/null
+++ b/src/applications/osgearth_boundarygen/boundarygen.cpp
@@ -0,0 +1,178 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2012 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+#include "BoundaryUtil"
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <osg/Notify>
+#include <osg/LineWidth>
+#include <osg/Point>
+#include <osg/PolygonOffset>
+#include <osg/MatrixTransform>
+#include <osgDB/ReadFile>
+#include <osgDB/WriteFile>
+#include <osgGA/StateSetManipulator>
+#include <osgGA/GUIEventHandler>
+#include <osgViewer/Viewer>
+#include <osgViewer/ViewerEventHandlers>
+
+
+int usage( char** argv, const std::string& msg )
+{
+  OSG_NOTICE << msg << "\n\n";
+  OSG_NOTICE 
+    << "osgEarth Boundary Generator Tool\n\n"
+    << "Generates boundary geometry that you can use with an osgEarth <mask> layer in order\n"
+    << "to stitch an external model into the terrain.\n\n"
+    << "USAGE: " << argv[0] << " [options] model_file\n"
+    << "           --out file_name     : output file for boundary geometry (default is boundary.txt)\n"
+    << "           --no-geocentric     : skip geocentric reprojection (for flat databases)\n"
+    << "           --convex-hull       : calculate a convex hull instead of a full boundary\n"
+    << "           --verbose           : print progress to console\n"
+    << "           --view              : show result in 3D window\n"
+    << std::endl;
+
+  
+  return -1;
+}
+
+int main(int argc, char** argv)
+{
+    osg::ArgumentParser arguments(&argc,argv);
+
+    std::string outFile;
+    if (!arguments.read("--out", outFile))
+      outFile = "boundary.txt";
+
+    bool geocentric = !arguments.read("--no-geocentric");
+    bool verbose = arguments.read("--verbose");
+    bool convexOnly = arguments.read("--convex-hull");
+    bool view = arguments.read("--view");
+
+    osg::Node* modelNode = osgDB::readNodeFiles( arguments );
+    if (!modelNode)
+        return usage( argv, "Unable to load model." );
+
+    osg::ref_ptr<osg::Vec3dArray> hull = BoundaryUtil::getBoundary(modelNode, geocentric, convexOnly);
+
+    if ( !outFile.empty() )
+    {
+      if (hull.valid())
+      {
+        if (verbose)
+          std::cout << std::endl << "hull.size() == " << hull->size() << std::endl;
+
+        std::ofstream outStream;
+        outStream.open(outFile.c_str());
+        if (outStream.fail())
+        {
+          std::cout << "Unable to open " << outFile << " for writing." << std::endl;
+        }
+        else
+        {
+          outStream << "POLYGON((";
+
+          osg::ref_ptr<osg::EllipsoidModel> em = new osg::EllipsoidModel();  
+
+          for (int i=0; i < (int)hull->size(); i++)
+          {
+            const osg::Vec3d& vert = (*hull.get())[i];
+
+            double lat, lon, height;
+            em->convertXYZToLatLongHeight( vert.x(), vert.y(), vert.z(), lat, lon, height );
+            lat = osg::RadiansToDegrees(lat);
+            lon = osg::RadiansToDegrees(lon);
+
+            if (verbose)
+              std::cout << "  hull[" << i << "] == " << lon << ", " << lat << ", " << height << std::endl;
+
+            outStream << std::setiosflags(std::ios_base::fixed) << (i > 0 ? ", " : "") << lon << " " << lat << " " << height;
+          }
+
+          outStream << "))";
+          outStream.close();
+
+          std::cout << "Boundary data written to " << outFile << std::endl;
+          
+          if (!convexOnly)
+            std::cout << "Boundary: " << (BoundaryUtil::simpleBoundaryTest(*hull) ? "VALID" : "INVALID") << std::endl;
+        }
+      }
+      else
+      {
+        std::cout << "Could not find boundary." << std::endl;
+      }
+    }
+
+    if (view)
+    {
+      osgViewer::Viewer viewer(arguments);
+
+      osg::BoundingSphered bs;
+      for( osg::Vec3dArray::iterator i = hull->begin(); i != hull->end(); ++i )
+          bs.expandBy( *i );
+
+      osg::MatrixTransform* xform = new osg::MatrixTransform();
+      xform->setMatrix( osg::Matrix::translate( bs.center() ) );
+
+      osg::Vec3Array* drawHull = new osg::Vec3Array();
+      for( osg::Vec3dArray::iterator i = hull->begin(); i != hull->end(); ++i )
+          drawHull->push_back( (*i) - bs.center() );
+
+      osg::Group* root = new osg::Group();
+      root->addChild( modelNode );
+      root->addChild( xform );
+      modelNode->getOrCreateStateSet()->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 );
+      
+      osg::Geometry* boundaryGeometry = new osg::Geometry();
+      boundaryGeometry->setVertexArray( drawHull );
+
+      osg::Vec4Array* colors = new osg::Vec4Array;
+      colors->push_back(osg::Vec4(1.0f,1.0f,0.0f,1.0f));
+      boundaryGeometry->setColorArray(colors);
+      boundaryGeometry->setColorBinding(osg::Geometry::BIND_OVERALL);
+      osg::StateSet* ss = boundaryGeometry->getOrCreateStateSet();
+      ss->setAttributeAndModes( new osg::LineWidth(1.0), 1 );
+      ss->setAttributeAndModes( new osg::Point(3.5), 1 );
+      ss->setMode( GL_LIGHTING, 0 );
+
+      boundaryGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP,0,drawHull->size()));
+      boundaryGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS,0,drawHull->size()));
+
+      osg::Geode* boundaryGeode = new osg::Geode();
+      boundaryGeode->addDrawable(boundaryGeometry);
+      xform->addChild(boundaryGeode);
+
+      viewer.setSceneData( root );
+
+      // add some stock OSG handlers:
+      viewer.addEventHandler(new osgViewer::StatsHandler());
+      viewer.addEventHandler(new osgViewer::WindowSizeHandler());
+      viewer.addEventHandler(new osgViewer::ThreadingHandler());
+      viewer.addEventHandler(new osgViewer::LODScaleHandler());
+      viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
+      viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
+
+      return viewer.run();
+    }
+
+    return 0;
+}
diff --git a/src/applications/osgearth_contour/osgearth_contour.cpp b/src/applications/osgearth_contour/osgearth_contour.cpp
index 81d333d..1d1479d 100644
--- a/src/applications/osgearth_contour/osgearth_contour.cpp
+++ b/src/applications/osgearth_contour/osgearth_contour.cpp
@@ -23,7 +23,7 @@
  */
 #include <osg/Notify>
 #include <osgViewer/Viewer>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/Registry>
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarthUtil/EarthManipulator>
diff --git a/src/applications/osgearth_controls/osgearth_controls.cpp b/src/applications/osgearth_controls/osgearth_controls.cpp
index adfadd4..70e1264 100644
--- a/src/applications/osgearth_controls/osgearth_controls.cpp
+++ b/src/applications/osgearth_controls/osgearth_controls.cpp
@@ -23,6 +23,7 @@
 #include <osgDB/ReadFile>
 #include <osgGA/GUIEventHandler>
 #include <osgViewer/Viewer>
+#include <osgEarth/Registry>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/Controls>
 #include <osgEarthSymbology/Color>
@@ -115,7 +116,7 @@ createControls( ControlCanvas* cs )
 
         // Add a text label:
         LabelControl* label = new LabelControl( "osgEarth Controls Toolkit" );
-        label->setFont( osgText::readFontFile( "arialbd.ttf" ) );
+        label->setFont( osgEarth::Registry::instance()->getDefaultFont() );
         label->setFontSize( 24.0f );
         label->setHorizAlign( Control::ALIGN_CENTER );
         label->setMargin( 5 );
diff --git a/src/applications/osgearth_measure/osgearth_measure.cpp b/src/applications/osgearth_measure/osgearth_measure.cpp
index 21999dd..83a659d 100644
--- a/src/applications/osgearth_measure/osgearth_measure.cpp
+++ b/src/applications/osgearth_measure/osgearth_measure.cpp
@@ -24,6 +24,7 @@
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/MapNode>
 #include <osgEarth/XmlUtils>
+#include <osgEarth/Registry>
 #include <osgEarth/Viewpoint>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/AutoClipPlaneHandler>
@@ -150,7 +151,7 @@ main(int argc, char** argv)
     // Add a text label:
     grid->setControl( 0, 0, new LabelControl("Distance:") );
     LabelControl* label = new LabelControl();
-    label->setFont( osgText::readFontFile( "arialbd.ttf" ) );
+    label->setFont( osgEarth::Registry::instance()->getDefaultFont() );
     label->setFontSize( 24.0f );
     label->setHorizAlign( Control::ALIGN_LEFT );    
     label->setText("click to measure");
diff --git a/src/applications/osgearth_package/osgearth_package.cpp b/src/applications/osgearth_package/osgearth_package.cpp
index 15723ec..9ee48bd 100644
--- a/src/applications/osgearth_package/osgearth_package.cpp
+++ b/src/applications/osgearth_package/osgearth_package.cpp
@@ -59,18 +59,12 @@ usage( const std::string& msg = "" )
         << "            <earth_file>                    : earth file defining layers to export (requied)\n"
         << "            --out <path>                    : root output folder of the TMS repo (required)\n"
         << "            [--bounds xmin ymin xmax ymax]* : bounds to package (in map coordinates; default=entire map)\n"
-        << "            [--max-level <num>]             : max LOD level for tiles (all layers; default=5)\n"
+        << "            [--max-level <num>]             : max LOD level for tiles (all layers; default=inf)\n"
         << "            [--out-earth <earthfile>]       : export an earth file referencing the new repo\n"
         << "            [--ext <extension>]             : overrides the image file extension (e.g. jpg)\n"
         << "            [--overwrite]                   : overwrite existing tiles\n"
         << "            [--keep-empties]                : writes out fully transparent image tiles (normally discarded)\n"
-#if 0
-        << std::endl
-        << "         --tfs                   : make a TFS repo" << std::endl
-        << "            [--out  <path>]      : root output folder of the TFS repo" << std::endl
-        << "            [--sort <attr>]      : name of attribute by which to sort features" << std::endl
-        << "            [--max  <num> ]      : target maximum # of features per tile" << std::endl
-#endif
+        << "            [--db-options]                : db options string to pass to the image writer in quotes (e.g., \"JPEG_QUALITY 60\")\n"
         << std::endl
         << "         [--quiet]               : suppress progress output" << std::endl;
 
@@ -117,9 +111,9 @@ makeTMS( osg::ArgumentParser& args )
 
     // find a .earth file on the command line
     std::string earthFile = findArgumentWithExtension(args, ".earth");
-    if ( earthFile.empty() )
+ /*   if ( earthFile.empty() )
         return usage( "Missing required .earth file" );
-
+        */
     // folder to which to write the TMS archive.
     std::string rootFolder;
     if ( !args.read( "--out", rootFolder ) )
@@ -134,6 +128,17 @@ makeTMS( osg::ArgumentParser& args )
     std::string outEarth;
     args.read("--out-earth", outEarth);
 
+    std::string dbOptions;
+    args.read("--db-options", dbOptions);
+    std::string::size_type n = 0;
+    while ((n=dbOptions.find('"', n))!=dbOptions.npos)
+    {
+        dbOptions.erase(n,1);
+    }
+
+    osg::ref_ptr<osgDB::Options> options = new osgDB::Options(dbOptions);
+
+
     std::vector< Bounds > bounds;
     // restrict packaging to user-specified bounds.    
     double xmin=DBL_MAX, ymin=DBL_MAX, xmax=DBL_MIN, ymax=DBL_MIN;
@@ -164,7 +169,7 @@ makeTMS( osg::ArgumentParser& args )
     Map* map = mapNode->getMap();
 
     // fire up a packager:
-    TMSPackager packager( map->getProfile() );
+    TMSPackager packager( map->getProfile(), options);
 
     packager.setVerbose( verbose );
     packager.setOverwrite( overwrite );
diff --git a/src/applications/osgearth_seed/osgearth_seed.cpp b/src/applications/osgearth_seed/osgearth_seed.cpp
index 20cb33b..318a030 100644
--- a/src/applications/osgearth_seed/osgearth_seed.cpp
+++ b/src/applications/osgearth_seed/osgearth_seed.cpp
@@ -24,6 +24,7 @@
 
 #include <osgEarth/Common>
 #include <osgEarth/Map>
+#include <osgEarth/MapFrame>
 #include <osgEarth/Cache>
 #include <osgEarth/CacheSeed>
 #include <osgEarth/MapNode>
diff --git a/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp b/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
index 6cc22bd..92b0c21 100644
--- a/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
+++ b/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
@@ -31,7 +31,7 @@
 #include <osgViewer/Viewer>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarthUtil/EarthManipulator>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/Registry>
 #include <osgEarthUtil/Controls>
 
@@ -46,9 +46,6 @@ int usage( const std::string& msg )
         << msg << "\n\n"
         << "USAGE: osgearth_shadercomp <earthfile> \n"
         << "           [--test1]    : Run the function injection test \n"
-        << "           [--test2]    : Run the shader nesting test \n"
-        << "           [--test3]    : Run the OVERRIDE mode test \n"
-        << "           [--test4]    : Run the PROTECTED mode test \n"
         << "           [--test5]    : Run the Program state set text \n"
         << std::endl;
 
@@ -97,110 +94,6 @@ namespace TEST_1
 
 //-------------------------------------------------------------------------
 
-// TESTS natual VP shader nesting. Even though a reddening shader is applied,
-// the built-in applyLighting shader should override it and take precedence.
-// Normal lighting should be used.
-namespace TEST_2
-{
-    char s_source[] =
-        "#version " GLSL_VERSION_STR "\n"
-        "void osgearth_frag_applyColoring( inout vec4 color ) { \n"
-        "    color.r = 1.0; \n"
-        "} \n";
-
-    osg::Group* run( osg::Node* earth )
-    {    
-        osg::Group* g1 = new osg::Group();
-        g1->addChild( earth );
-
-        osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
-        vp->setShader( "osgearth_frag_applyColoring", new osg::Shader(osg::Shader::FRAGMENT, s_source) );
-        g1->getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
-
-        return g1;
-    }
-}
-
-//-------------------------------------------------------------------------
-
-// TESTS the use of the OVERRIDE qualifier. Same as TEST_2 shader, but the "reddening"
-// shader should pre-empty the built-in shader because of the OVERRIDE mode.
-namespace TEST_3
-{
-    char s_source[] =
-        "#version " GLSL_VERSION_STR "\n"
-        "void osgearth_frag_applyColoring( inout vec4 color ) { \n"
-        "    color = vec4(1.0, 0.0, 0.0, 1.0); \n"
-        "} \n";
-
-    osg::Group* run( osg::Node* earth )
-    {    
-        osg::Group* g1 = new osg::Group();
-        g1->addChild( earth );
-
-        osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
-
-        // NOTE the use of OVERRIDE; this prevents subordinate VPs from replacing the 
-        // function (unless marked as PROTECTED).
-        vp->setShader( 
-            "osgearth_frag_applyColoring",
-            new osg::Shader(osg::Shader::FRAGMENT, s_source),
-            osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
-
-        g1->getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
-
-        return g1;
-    }
-}
-
-//-------------------------------------------------------------------------
-
-// TESTS the use of the PROTECTED qualifier. Same as TEST_3 shader, but the "reddening"
-// shader (which has its OVERRIDE mode set) has a subordinate shader with its PROTECTED
-// mode set, which nullifies the effect of the OVERRIDE.
-namespace TEST_4
-{
-    char s_source_1[] =
-        "#version " GLSL_VERSION_STR "\n"
-        "void osgearth_frag_applyColoring( inout vec4 color ) { \n"
-        "    color = vec4(1.0, 0.0, 0.0, 1.0); \n"
-        "} \n";
-    
-    char s_source_2[] =
-        "#version " GLSL_VERSION_STR "\n"
-        "void osgearth_frag_applyColoring( inout vec4 color ) { \n"
-        "    color = vec4(0.0, 0.0, 1.0, 1.0); \n"
-        "} \n";
-
-    osg::Group* run( osg::Node* earth )
-    {    
-        // Insert a Shader in OVERRIDE mode:
-        osg::Group* g1 = new osg::Group();
-        osgEarth::VirtualProgram* vp1 = new osgEarth::VirtualProgram();
-        vp1->setShader( 
-            "osgearth_frag_applyColoring",
-            new osg::Shader(osg::Shader::FRAGMENT, s_source_1),
-            osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
-        g1->getOrCreateStateSet()->setAttributeAndModes( vp1, osg::StateAttribute::ON );
-
-        // Insert a subordinate shader in PROTECTED mode:
-        osg::Group* g2 = new osg::Group();
-        osgEarth::VirtualProgram* vp2 = new osgEarth::VirtualProgram();
-        vp2->setShader(
-            "osgearth_frag_applyColoring",
-            new osg::Shader(osg::Shader::FRAGMENT, s_source_2),
-            osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE );
-        g2->getOrCreateStateSet()->setAttributeAndModes( vp2, osg::StateAttribute::ON );
-
-        g1->addChild( g2 );
-        g2->addChild( earth );
-        return g1;
-    }
-}
-
-//-------------------------------------------------------------------------
-
-// 
 namespace TEST_5
 {
     char s_vert[] =
@@ -256,8 +149,6 @@ namespace TEST_5
 
         osg::Group* root = new osg::Group();
         root->getOrCreateStateSet()->setRenderBinDetails( 0, "TraversalOrderBin" );
-        //n1->getOrCreateStateSet()->setRenderBinDetails( 2, "RenderBin" );
-        //n2->getOrCreateStateSet()->setRenderBinDetails( 1, "RenderBin" );
         root->getOrCreateStateSet()->setMode(GL_LIGHTING,0);
 
         root->addChild( n1 );
@@ -276,10 +167,13 @@ int main(int argc, char** argv)
     osgViewer::Viewer viewer(arguments);
 
     bool test1 = arguments.read("--test1");
-    bool test2 = arguments.read("--test2");
-    bool test3 = arguments.read("--test3");
-    bool test4 = arguments.read("--test4");
     bool test5 = arguments.read("--test5");
+    bool ok    = test1 || test5; 
+
+    if ( !ok )
+    {
+        return usage("");
+    }
 
     if ( !test5 )
     {
@@ -300,21 +194,6 @@ int main(int argc, char** argv)
             viewer.setSceneData( TEST_1::run(earthNode) );
             label->setText( "Function injection test: the map should appear hazy." );
         }
-        else if ( test2 )
-        {
-            viewer.setSceneData( TEST_2::run(earthNode) );
-            label->setText( "Shader nesting test: the map should appear normally." );
-        }
-        else if ( test3 )
-        {
-            viewer.setSceneData( TEST_3::run(earthNode) );
-            label->setText( "Shader override test: the map should appear red." );
-        }
-        else if ( test4 )
-        {
-            viewer.setSceneData( TEST_4::run(earthNode) );
-            label->setText( "Shader protected test: the map should appear blue." );
-        }
     }
     else // if ( test5 )
     {
diff --git a/src/applications/osgearth_shadow/osgearth_shadow.cpp b/src/applications/osgearth_shadow/osgearth_shadow.cpp
index 9488f9c..6b1d188 100644
--- a/src/applications/osgearth_shadow/osgearth_shadow.cpp
+++ b/src/applications/osgearth_shadow/osgearth_shadow.cpp
@@ -124,7 +124,7 @@ int main(int argc, char** argv)
     if (!skyNode)
     {
         OE_NOTICE << "Please run with options --sky to enable the SkyNode" << std::endl;
-        exit(1);
+        //exit(1);
     }
 
     // Prevent terrain skirts (or other "secondary geometry") from casting shadows
diff --git a/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp b/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
index 932f85f..09a61b8 100644
--- a/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
+++ b/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
@@ -28,6 +28,7 @@
 #include <osgEarthUtil/AutoClipPlaneHandler>
 #include <osgEarthUtil/TerrainProfile>
 #include <osgEarth/GeoMath>
+#include <osgEarth/Registry>
 #include <osgEarthFeatures/Feature>
 #include <osgEarthAnnotation/FeatureNode>
 #include <osgText/Text>
@@ -93,7 +94,7 @@ public:
         _profileCalculator->addChangedCallback( _graphChangedCallback.get() );
 
         float textSize = 8;
-        osg::ref_ptr< osgText::Font> font = osgText::readFontFile( "arialbd.ttf" );
+        osg::ref_ptr< osgText::Font> font = osgEarth::Registry::instance()->getDefaultFont();
 
         osg::Vec4 textColor = osg::Vec4f(1,0,0,1);
         
diff --git a/src/applications/osgearth_toc/osgearth_toc.cpp b/src/applications/osgearth_toc/osgearth_toc.cpp
index 7addf24..5a881fe 100644
--- a/src/applications/osgearth_toc/osgearth_toc.cpp
+++ b/src/applications/osgearth_toc/osgearth_toc.cpp
@@ -17,6 +17,7 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include <osgEarth/Map>
+#include <osgEarth/MapFrame>
 #include <osgEarth/MapNode>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/Controls>
diff --git a/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp b/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp
index 23af1de..779cf9e 100644
--- a/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp
+++ b/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp
@@ -23,7 +23,7 @@
  */
 #include <osg/Notify>
 #include <osgViewer/Viewer>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/Registry>
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarthUtil/EarthManipulator>
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..23e708c
--- /dev/null
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
@@ -0,0 +1,1044 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		90283E5615C6FB3E00620EEF /* tests in Resources */ = {isa = PBXBuildFile; fileRef = 90283E5415C6FB3E00620EEF /* tests */; };
+		90283E5715C6FB3E00620EEF /* data in Resources */ = {isa = PBXBuildFile; fileRef = 90283E5515C6FB3E00620EEF /* data */; };
+		90283E5D15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90283E5C15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a */; };
+		903B45DB15C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 903B45D915C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp */; };
+		9048FB2415FA9DE50012C900 /* libosgdb_tiff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */; };
+		9051000115B1EDFD00D9ABD3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051000015B1EDFD00D9ABD3 /* Foundation.framework */; };
+		9051000315B1EDFD00D9ABD3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051000215B1EDFD00D9ABD3 /* CoreGraphics.framework */; };
+		9051000715B1EDFD00D9ABD3 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051000615B1EDFD00D9ABD3 /* OpenGLES.framework */; };
+		9051000D15B1EDFD00D9ABD3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9051000B15B1EDFD00D9ABD3 /* InfoPlist.strings */; };
+		9051000F15B1EDFD00D9ABD3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9051000E15B1EDFD00D9ABD3 /* main.m */; };
+		9051001315B1EDFD00D9ABD3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9051001215B1EDFD00D9ABD3 /* AppDelegate.m */; };
+		9051001A15B1EDFD00D9ABD3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9051001915B1EDFD00D9ABD3 /* ViewController.m */; };
+		9051001D15B1EDFD00D9ABD3 /* ViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9051001B15B1EDFD00D9ABD3 /* ViewController_iPhone.xib */; };
+		9051002015B1EDFD00D9ABD3 /* ViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9051001E15B1EDFD00D9ABD3 /* ViewController_iPad.xib */; };
+		905100C315B20AC600D9ABD3 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100C215B20AC600D9ABD3 /* ImageIO.framework */; };
+		905100C515B20AD100D9ABD3 /* CoreImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100C415B20AD100D9ABD3 /* CoreImage.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+		905100CB15B2101E00D9ABD3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100CA15B2101E00D9ABD3 /* QuartzCore.framework */; };
+		905100CD15B217A800D9ABD3 /* libc++.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100CC15B217A800D9ABD3 /* libc++.dylib */; };
+		905100CF15B217B500D9ABD3 /* libz.1.1.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100CE15B217B500D9ABD3 /* libz.1.1.3.dylib */; };
+		905100D115B2185000D9ABD3 /* libiconv.2.4.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100D015B2185000D9ABD3 /* libiconv.2.4.0.dylib */; };
+		9051FFFF15B1EDFD00D9ABD3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051FFFE15B1EDFD00D9ABD3 /* UIKit.framework */; };
+		907D033915B86F8700575110 /* libOpenThreads.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DC15B86F8700575110 /* libOpenThreads.a */; };
+		907D033A15B86F8700575110 /* libosg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DD15B86F8700575110 /* libosg.a */; };
+		907D033B15B86F8700575110 /* libosgAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DE15B86F8700575110 /* libosgAnimation.a */; };
+		907D033C15B86F8700575110 /* libosgdb_3dc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DF15B86F8700575110 /* libosgdb_3dc.a */; };
+		907D033D15B86F8700575110 /* libosgdb_3ds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E015B86F8700575110 /* libosgdb_3ds.a */; };
+		907D033E15B86F8700575110 /* libosgdb_ac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E115B86F8700575110 /* libosgdb_ac.a */; };
+		907D033F15B86F8700575110 /* libosgdb_bmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E215B86F8700575110 /* libosgdb_bmp.a */; };
+		907D034015B86F8700575110 /* libosgdb_bsp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E315B86F8700575110 /* libosgdb_bsp.a */; };
+		907D034115B86F8700575110 /* libosgdb_bvh.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E415B86F8700575110 /* libosgdb_bvh.a */; };
+		907D034215B86F8700575110 /* libosgdb_cfg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E515B86F8700575110 /* libosgdb_cfg.a */; };
+		907D034315B86F8700575110 /* libosgdb_curl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E615B86F8700575110 /* libosgdb_curl.a */; };
+		907D034415B86F8700575110 /* libosgdb_dds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E715B86F8700575110 /* libosgdb_dds.a */; };
+		907D034515B86F8700575110 /* libosgdb_deprecated_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */; };
+		907D034615B86F8700575110 /* libosgdb_deprecated_osganimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E915B86F8700575110 /* libosgdb_deprecated_osganimation.a */; };
+		907D034715B86F8700575110 /* libosgdb_deprecated_osgfx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EA15B86F8700575110 /* libosgdb_deprecated_osgfx.a */; };
+		907D034815B86F8700575110 /* libosgdb_deprecated_osgparticle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EB15B86F8700575110 /* libosgdb_deprecated_osgparticle.a */; };
+		907D034915B86F8700575110 /* libosgdb_deprecated_osgshadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EC15B86F8700575110 /* libosgdb_deprecated_osgshadow.a */; };
+		907D034A15B86F8700575110 /* libosgdb_deprecated_osgsim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02ED15B86F8700575110 /* libosgdb_deprecated_osgsim.a */; };
+		907D034B15B86F8700575110 /* libosgdb_deprecated_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EE15B86F8700575110 /* libosgdb_deprecated_osgterrain.a */; };
+		907D034C15B86F8700575110 /* libosgdb_deprecated_osgtext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EF15B86F8700575110 /* libosgdb_deprecated_osgtext.a */; };
+		907D034D15B86F8700575110 /* libosgdb_deprecated_osgviewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F015B86F8700575110 /* libosgdb_deprecated_osgviewer.a */; };
+		907D034E15B86F8700575110 /* libosgdb_deprecated_osgvolume.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F115B86F8700575110 /* libosgdb_deprecated_osgvolume.a */; };
+		907D034F15B86F8700575110 /* libosgdb_deprecated_osgwidget.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F215B86F8700575110 /* libosgdb_deprecated_osgwidget.a */; };
+		907D035015B86F8700575110 /* libosgdb_dot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F315B86F8700575110 /* libosgdb_dot.a */; };
+		907D035115B86F8700575110 /* libosgdb_dw.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F415B86F8700575110 /* libosgdb_dw.a */; };
+		907D035215B86F8700575110 /* libosgdb_dxf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F515B86F8700575110 /* libosgdb_dxf.a */; };
+		907D035315B86F8700575110 /* libosgdb_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F615B86F8700575110 /* libosgdb_freetype.a */; };
+		907D035415B86F8700575110 /* libosgdb_gdal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F715B86F8700575110 /* libosgdb_gdal.a */; };
+		907D035515B86F8700575110 /* libosgdb_geo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F815B86F8700575110 /* libosgdb_geo.a */; };
+		907D035615B86F8700575110 /* libosgdb_glsl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F915B86F8700575110 /* libosgdb_glsl.a */; };
+		907D035715B86F8700575110 /* libosgdb_gz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FA15B86F8700575110 /* libosgdb_gz.a */; };
+		907D035815B86F8700575110 /* libosgdb_hdr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FB15B86F8700575110 /* libosgdb_hdr.a */; };
+		907D035915B86F8700575110 /* libosgdb_imageio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FC15B86F8700575110 /* libosgdb_imageio.a */; };
+		907D035A15B86F8700575110 /* libosgdb_ive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FD15B86F8700575110 /* libosgdb_ive.a */; };
+		907D035B15B86F8700575110 /* libosgdb_logo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FE15B86F8700575110 /* libosgdb_logo.a */; };
+		907D035C15B86F8700575110 /* libosgdb_lwo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FF15B86F8700575110 /* libosgdb_lwo.a */; };
+		907D035D15B86F8700575110 /* libosgdb_lws.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030015B86F8700575110 /* libosgdb_lws.a */; };
+		907D035E15B86F8700575110 /* libosgdb_md2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030115B86F8700575110 /* libosgdb_md2.a */; };
+		907D035F15B86F8700575110 /* libosgdb_mdl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030215B86F8700575110 /* libosgdb_mdl.a */; };
+		907D036015B86F8700575110 /* libosgdb_normals.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030315B86F8700575110 /* libosgdb_normals.a */; };
+		907D036115B86F8700575110 /* libosgdb_obj.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030415B86F8700575110 /* libosgdb_obj.a */; };
+		907D036215B86F8700575110 /* libosgdb_ogr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030515B86F8700575110 /* libosgdb_ogr.a */; };
+		907D036315B86F8700575110 /* libosgdb_openflight.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030615B86F8700575110 /* libosgdb_openflight.a */; };
+		907D036415B86F8700575110 /* libosgdb_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030715B86F8700575110 /* libosgdb_osg.a */; };
+		907D036515B86F8700575110 /* libosgdb_osga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030815B86F8700575110 /* libosgdb_osga.a */; };
+		907D036615B86F8700575110 /* libosgdb_osgshadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030915B86F8700575110 /* libosgdb_osgshadow.a */; };
+		907D036715B86F8700575110 /* libosgdb_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030A15B86F8700575110 /* libosgdb_osgterrain.a */; };
+		907D036815B86F8700575110 /* libosgdb_osgtgz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030B15B86F8700575110 /* libosgdb_osgtgz.a */; };
+		907D036915B86F8700575110 /* libosgdb_osgviewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030C15B86F8700575110 /* libosgdb_osgviewer.a */; };
+		907D036A15B86F8700575110 /* libosgdb_p3d.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030D15B86F8700575110 /* libosgdb_p3d.a */; };
+		907D036B15B86F8700575110 /* libosgdb_pic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030E15B86F8700575110 /* libosgdb_pic.a */; };
+		907D036C15B86F8700575110 /* libosgdb_ply.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030F15B86F8700575110 /* libosgdb_ply.a */; };
+		907D036D15B86F8700575110 /* libosgdb_pnm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031015B86F8700575110 /* libosgdb_pnm.a */; };
+		907D036E15B86F8700575110 /* libosgdb_pov.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031115B86F8700575110 /* libosgdb_pov.a */; };
+		907D036F15B86F8700575110 /* libosgdb_pvr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031215B86F8700575110 /* libosgdb_pvr.a */; };
+		907D037015B86F8700575110 /* libosgdb_revisions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031315B86F8700575110 /* libosgdb_revisions.a */; };
+		907D037115B86F8700575110 /* libosgdb_rgb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031415B86F8700575110 /* libosgdb_rgb.a */; };
+		907D037215B86F8700575110 /* libosgdb_rot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031515B86F8700575110 /* libosgdb_rot.a */; };
+		907D037315B86F8700575110 /* libosgdb_scale.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031615B86F8700575110 /* libosgdb_scale.a */; };
+		907D037415B86F8700575110 /* libosgdb_serializers_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031715B86F8700575110 /* libosgdb_serializers_osg.a */; };
+		907D037515B86F8700575110 /* libosgdb_serializers_osganimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031815B86F8700575110 /* libosgdb_serializers_osganimation.a */; };
+		907D037615B86F8700575110 /* libosgdb_serializers_osgfx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031915B86F8700575110 /* libosgdb_serializers_osgfx.a */; };
+		907D037715B86F8700575110 /* libosgdb_serializers_osgmanipulator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031A15B86F8700575110 /* libosgdb_serializers_osgmanipulator.a */; };
+		907D037815B86F8700575110 /* libosgdb_serializers_osgparticle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031B15B86F8700575110 /* libosgdb_serializers_osgparticle.a */; };
+		907D037915B86F8700575110 /* libosgdb_serializers_osgshadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031C15B86F8700575110 /* libosgdb_serializers_osgshadow.a */; };
+		907D037A15B86F8700575110 /* libosgdb_serializers_osgsim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031D15B86F8700575110 /* libosgdb_serializers_osgsim.a */; };
+		907D037B15B86F8700575110 /* libosgdb_serializers_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031E15B86F8700575110 /* libosgdb_serializers_osgterrain.a */; };
+		907D037C15B86F8700575110 /* libosgdb_serializers_osgtext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031F15B86F8700575110 /* libosgdb_serializers_osgtext.a */; };
+		907D037D15B86F8700575110 /* libosgdb_serializers_osgvolume.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032015B86F8700575110 /* libosgdb_serializers_osgvolume.a */; };
+		907D037E15B86F8700575110 /* libosgdb_shp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032115B86F8700575110 /* libosgdb_shp.a */; };
+		907D037F15B86F8700575110 /* libosgdb_stl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032215B86F8700575110 /* libosgdb_stl.a */; };
+		907D038015B86F8700575110 /* libosgdb_tga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032315B86F8700575110 /* libosgdb_tga.a */; };
+		907D038115B86F8700575110 /* libosgdb_tgz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032415B86F8700575110 /* libosgdb_tgz.a */; };
+		907D038215B86F8700575110 /* libosgdb_trans.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032515B86F8700575110 /* libosgdb_trans.a */; };
+		907D038315B86F8700575110 /* libosgdb_txf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032615B86F8700575110 /* libosgdb_txf.a */; };
+		907D038415B86F8700575110 /* libosgdb_txp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032715B86F8700575110 /* libosgdb_txp.a */; };
+		907D038515B86F8700575110 /* libosgdb_vtf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032815B86F8700575110 /* libosgdb_vtf.a */; };
+		907D038615B86F8700575110 /* libosgdb_x.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032915B86F8700575110 /* libosgdb_x.a */; };
+		907D038715B86F8700575110 /* libosgdb_zip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032A15B86F8700575110 /* libosgdb_zip.a */; };
+		907D038815B86F8700575110 /* libosgDB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032B15B86F8700575110 /* libosgDB.a */; };
+		907D038915B86F8700575110 /* libosgFX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032C15B86F8700575110 /* libosgFX.a */; };
+		907D038A15B86F8700575110 /* libosgGA.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032D15B86F8700575110 /* libosgGA.a */; };
+		907D038B15B86F8700575110 /* libosgManipulator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032E15B86F8700575110 /* libosgManipulator.a */; };
+		907D038C15B86F8700575110 /* libosgParticle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032F15B86F8700575110 /* libosgParticle.a */; };
+		907D038D15B86F8700575110 /* libosgPresentation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033015B86F8700575110 /* libosgPresentation.a */; };
+		907D038E15B86F8700575110 /* libosgShadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033115B86F8700575110 /* libosgShadow.a */; };
+		907D038F15B86F8700575110 /* libosgSim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033215B86F8700575110 /* libosgSim.a */; };
+		907D039015B86F8700575110 /* libosgTerrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033315B86F8700575110 /* libosgTerrain.a */; };
+		907D039115B86F8700575110 /* libosgText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033415B86F8700575110 /* libosgText.a */; };
+		907D039215B86F8700575110 /* libosgUtil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033515B86F8700575110 /* libosgUtil.a */; };
+		907D039315B86F8700575110 /* libosgViewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033615B86F8700575110 /* libosgViewer.a */; };
+		907D039415B86F8700575110 /* libosgVolume.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033715B86F8700575110 /* libosgVolume.a */; };
+		907D039515B86F8700575110 /* libosgWidget.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033815B86F8700575110 /* libosgWidget.a */; };
+		907D03BD15B86F9E00575110 /* libosgdb_kml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039715B86F9E00575110 /* libosgdb_kml.a */; };
+		907D03BE15B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039815B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a */; };
+		907D03BF15B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039915B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a */; };
+		907D03C015B86F9E00575110 /* libosgdb_osgearth_tms.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039A15B86F9E00575110 /* libosgdb_osgearth_tms.a */; };
+		907D03C115B86F9E00575110 /* libosgdb_osgearth_wms.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039B15B86F9E00575110 /* libosgdb_osgearth_wms.a */; };
+		907D03C215B86F9E00575110 /* libosgdb_osgearth_label_overlay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039C15B86F9E00575110 /* libosgdb_osgearth_label_overlay.a */; };
+		907D03C315B86F9E00575110 /* libosgdb_osgearth_xyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039D15B86F9E00575110 /* libosgdb_osgearth_xyz.a */; };
+		907D03C415B86F9E00575110 /* libosgEarthUtil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039E15B86F9E00575110 /* libosgEarthUtil.a */; };
+		907D03C515B86F9E00575110 /* libosgdb_osgearth_label_annotation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039F15B86F9E00575110 /* libosgdb_osgearth_label_annotation.a */; };
+		907D03C615B86F9E00575110 /* libosgdb_osgearth_mask_feature.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A015B86F9E00575110 /* libosgdb_osgearth_mask_feature.a */; };
+		907D03C715B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A115B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a */; };
+		907D03C815B86F9E00575110 /* libosgEarthAnnotation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A215B86F9E00575110 /* libosgEarthAnnotation.a */; };
+		907D03C915B86F9E00575110 /* libosgdb_osgearth_agglite.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A315B86F9E00575110 /* libosgdb_osgearth_agglite.a */; };
+		907D03CA15B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A415B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a */; };
+		907D03CB15B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A515B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a */; };
+		907D03CC15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A615B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a */; };
+		907D03CD15B86F9E00575110 /* libosgdb_osgearth_model_simple.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A715B86F9E00575110 /* libosgdb_osgearth_model_simple.a */; };
+		907D03CE15B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A815B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a */; };
+		907D03CF15B86F9E00575110 /* libosgEarthFeatures.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A915B86F9E00575110 /* libosgEarthFeatures.a */; };
+		907D03D015B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AA15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a */; };
+		907D03D115B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AB15B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a */; };
+		907D03D215B86F9E00575110 /* libosgdb_osgearth_debug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AC15B86F9E00575110 /* libosgdb_osgearth_debug.a */; };
+		907D03D315B86F9E00575110 /* libosgdb_osgearth_mbtiles.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AD15B86F9E00575110 /* libosgdb_osgearth_mbtiles.a */; };
+		907D03D415B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AE15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a */; };
+		907D03D515B86F9E00575110 /* libosgdb_osgearth_tileservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AF15B86F9E00575110 /* libosgdb_osgearth_tileservice.a */; };
+		907D03D615B86F9E00575110 /* libosgdb_osgearth_yahoo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B015B86F9E00575110 /* libosgdb_osgearth_yahoo.a */; };
+		907D03D715B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B115B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a */; };
+		907D03D815B86F9E00575110 /* libosgdb_osgearth_tilecache.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B215B86F9E00575110 /* libosgdb_osgearth_tilecache.a */; };
+		907D03D915B86F9E00575110 /* libosgdb_osgearth_wcs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B315B86F9E00575110 /* libosgdb_osgearth_wcs.a */; };
+		907D03DA15B86F9E00575110 /* libosgEarthSymbology.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B415B86F9E00575110 /* libosgEarthSymbology.a */; };
+		907D03DB15B86F9F00575110 /* libosgdb_osgearth_gdal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B515B86F9E00575110 /* libosgdb_osgearth_gdal.a */; };
+		907D03DC15B86F9F00575110 /* libosgdb_osgearth_refresh.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B615B86F9E00575110 /* libosgdb_osgearth_refresh.a */; };
+		907D03DD15B86F9F00575110 /* libosgdb_osgearth_vpb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B715B86F9E00575110 /* libosgdb_osgearth_vpb.a */; };
+		907D03DE15B86F9F00575110 /* libosgdb_earth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B815B86F9E00575110 /* libosgdb_earth.a */; };
+		907D03DF15B86F9F00575110 /* libosgdb_osgearth_cache_filesystem.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B915B86F9E00575110 /* libosgdb_osgearth_cache_filesystem.a */; };
+		907D03E015B86F9F00575110 /* libosgdb_osgearth_arcgis.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03BA15B86F9E00575110 /* libosgdb_osgearth_arcgis.a */; };
+		907D03E115B86F9F00575110 /* libosgdb_osgearth_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03BB15B86F9E00575110 /* libosgdb_osgearth_osg.a */; };
+		907D03E215B86F9F00575110 /* libosgEarth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03BC15B86F9E00575110 /* libosgEarth.a */; };
+		907D03FA15B8C31A00575110 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03F915B8C31A00575110 /* libsqlite3.dylib */; };
+		907D0A8E15B8CEBE00575110 /* libGEOS_3.2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D0A8D15B8CEBE00575110 /* libGEOS_3.2.a */; };
+		907D0A9115B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 907D0A8F15B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp */; };
+		90A0DD6D15B7BAF9004FACEE /* libFreeType_iphone_universal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD6C15B7BAF9004FACEE /* libFreeType_iphone_universal.a */; };
+		90A0DD6F15B7BB50004FACEE /* libproj.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD6E15B7BB50004FACEE /* libproj.a */; };
+		90A0DD7115B7BB64004FACEE /* libgdal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD7015B7BB64004FACEE /* libgdal.a */; };
+		90A0DD7615B7BBA4004FACEE /* libcurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD7515B7BBA4004FACEE /* libcurl.a */; };
+		90B8676615C8894900F5CDC3 /* StartViewerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 90B8676415C8894900F5CDC3 /* StartViewerController.m */; };
+		90B8676715C8894900F5CDC3 /* StartViewerController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90B8676515C8894900F5CDC3 /* StartViewerController.xib */; };
+		90DABDDC15CEFF9700D0F609 /* moon_1024x512.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 90DABDDB15CEFF9700D0F609 /* moon_1024x512.jpg */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		90283E5415C6FB3E00620EEF /* tests */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tests; path = ../../../../tests; sourceTree = "<group>"; };
+		90283E5515C6FB3E00620EEF /* data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = data; path = ../../../../data; sourceTree = "<group>"; };
+		90283E5C15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_engine_quadtree.a; path = ../../../lib/Release/libosgdb_osgearth_engine_quadtree.a; sourceTree = "<group>"; };
+		903B45D915C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EarthMultiTouchManipulator.cpp; sourceTree = "<group>"; };
+		903B45DA15C0DE9F00F7702B /* EarthMultiTouchManipulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EarthMultiTouchManipulator.h; sourceTree = "<group>"; };
+		9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tiff.a; path = "../../../../osg-ios-gles2/lib/libosgdb_tiff.a"; sourceTree = "<group>"; };
+		9051000015B1EDFD00D9ABD3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		9051000215B1EDFD00D9ABD3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+		9051000615B1EDFD00D9ABD3 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
+		9051000A15B1EDFD00D9ABD3 /* osgEarthViewerIOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "osgEarthViewerIOS-Info.plist"; sourceTree = "<group>"; };
+		9051000C15B1EDFD00D9ABD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		9051000E15B1EDFD00D9ABD3 /* main.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = main.m; sourceTree = "<group>"; };
+		9051001015B1EDFD00D9ABD3 /* osgEarthViewerIOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "osgEarthViewerIOS-Prefix.pch"; sourceTree = "<group>"; };
+		9051001115B1EDFD00D9ABD3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		9051001215B1EDFD00D9ABD3 /* AppDelegate.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = AppDelegate.m; sourceTree = "<group>"; };
+		9051001815B1EDFD00D9ABD3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+		9051001915B1EDFD00D9ABD3 /* ViewController.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = ViewController.m; sourceTree = "<group>"; };
+		9051001C15B1EDFD00D9ABD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPhone.xib; sourceTree = "<group>"; };
+		9051001F15B1EDFD00D9ABD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPad.xib; sourceTree = "<group>"; };
+		905100C215B20AC600D9ABD3 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
+		905100C415B20AD100D9ABD3 /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; };
+		905100C615B20B1D00D9ABD3 /* osgPlugins.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osgPlugins.h; sourceTree = "<group>"; };
+		905100CA15B2101E00D9ABD3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+		905100CC15B217A800D9ABD3 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; };
+		905100CE15B217B500D9ABD3 /* libz.1.1.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.1.3.dylib; path = usr/lib/libz.1.1.3.dylib; sourceTree = SDKROOT; };
+		905100D015B2185000D9ABD3 /* libiconv.2.4.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.2.4.0.dylib; path = usr/lib/libiconv.2.4.0.dylib; sourceTree = SDKROOT; };
+		9051FFFA15B1EDFD00D9ABD3 /* osgEarth.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = osgEarth.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		9051FFFE15B1EDFD00D9ABD3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+		907D02DC15B86F8700575110 /* libOpenThreads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libOpenThreads.a; path = "../../../../osg-ios-gles2/lib/libOpenThreads.a"; sourceTree = "<group>"; };
+		907D02DD15B86F8700575110 /* libosg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosg.a; path = "../../../../osg-ios-gles2/lib/libosg.a"; sourceTree = "<group>"; };
+		907D02DE15B86F8700575110 /* libosgAnimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgAnimation.a; path = "../../../../osg-ios-gles2/lib/libosgAnimation.a"; sourceTree = "<group>"; };
+		907D02DF15B86F8700575110 /* libosgdb_3dc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_3dc.a; path = "../../../../osg-ios-gles2/lib/libosgdb_3dc.a"; sourceTree = "<group>"; };
+		907D02E015B86F8700575110 /* libosgdb_3ds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_3ds.a; path = "../../../../osg-ios-gles2/lib/libosgdb_3ds.a"; sourceTree = "<group>"; };
+		907D02E115B86F8700575110 /* libosgdb_ac.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ac.a; path = "../../../../osg-ios-gles2/lib/libosgdb_ac.a"; sourceTree = "<group>"; };
+		907D02E215B86F8700575110 /* libosgdb_bmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bmp.a; path = "../../../../osg-ios-gles2/lib/libosgdb_bmp.a"; sourceTree = "<group>"; };
+		907D02E315B86F8700575110 /* libosgdb_bsp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bsp.a; path = "../../../../osg-ios-gles2/lib/libosgdb_bsp.a"; sourceTree = "<group>"; };
+		907D02E415B86F8700575110 /* libosgdb_bvh.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bvh.a; path = "../../../../osg-ios-gles2/lib/libosgdb_bvh.a"; sourceTree = "<group>"; };
+		907D02E515B86F8700575110 /* libosgdb_cfg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_cfg.a; path = "../../../../osg-ios-gles2/lib/libosgdb_cfg.a"; sourceTree = "<group>"; };
+		907D02E615B86F8700575110 /* libosgdb_curl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_curl.a; path = "../../../../osg-ios-gles2/lib/libosgdb_curl.a"; sourceTree = "<group>"; };
+		907D02E715B86F8700575110 /* libosgdb_dds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dds.a; path = "../../../../osg-ios-gles2/lib/libosgdb_dds.a"; sourceTree = "<group>"; };
+		907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osg.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osg.a"; sourceTree = "<group>"; };
+		907D02E915B86F8700575110 /* libosgdb_deprecated_osganimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osganimation.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osganimation.a"; sourceTree = "<group>"; };
+		907D02EA15B86F8700575110 /* libosgdb_deprecated_osgfx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgfx.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgfx.a"; sourceTree = "<group>"; };
+		907D02EB15B86F8700575110 /* libosgdb_deprecated_osgparticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgparticle.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgparticle.a"; sourceTree = "<group>"; };
+		907D02EC15B86F8700575110 /* libosgdb_deprecated_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgshadow.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgshadow.a"; sourceTree = "<group>"; };
+		907D02ED15B86F8700575110 /* libosgdb_deprecated_osgsim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgsim.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgsim.a"; sourceTree = "<group>"; };
+		907D02EE15B86F8700575110 /* libosgdb_deprecated_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgterrain.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgterrain.a"; sourceTree = "<group>"; };
+		907D02EF15B86F8700575110 /* libosgdb_deprecated_osgtext.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgtext.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgtext.a"; sourceTree = "<group>"; };
+		907D02F015B86F8700575110 /* libosgdb_deprecated_osgviewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgviewer.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgviewer.a"; sourceTree = "<group>"; };
+		907D02F115B86F8700575110 /* libosgdb_deprecated_osgvolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgvolume.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgvolume.a"; sourceTree = "<group>"; };
+		907D02F215B86F8700575110 /* libosgdb_deprecated_osgwidget.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgwidget.a; path = "../../../../osg-ios-gles2/lib/libosgdb_deprecated_osgwidget.a"; sourceTree = "<group>"; };
+		907D02F315B86F8700575110 /* libosgdb_dot.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dot.a; path = "../../../../osg-ios-gles2/lib/libosgdb_dot.a"; sourceTree = "<group>"; };
+		907D02F415B86F8700575110 /* libosgdb_dw.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dw.a; path = "../../../../osg-ios-gles2/lib/libosgdb_dw.a"; sourceTree = "<group>"; };
+		907D02F515B86F8700575110 /* libosgdb_dxf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dxf.a; path = "../../../../osg-ios-gles2/lib/libosgdb_dxf.a"; sourceTree = "<group>"; };
+		907D02F615B86F8700575110 /* libosgdb_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_freetype.a; path = "../../../../osg-ios-gles2/lib/libosgdb_freetype.a"; sourceTree = "<group>"; };
+		907D02F715B86F8700575110 /* libosgdb_gdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_gdal.a; path = "../../../../osg-ios-gles2/lib/libosgdb_gdal.a"; sourceTree = "<group>"; };
+		907D02F815B86F8700575110 /* libosgdb_geo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_geo.a; path = "../../../../osg-ios-gles2/lib/libosgdb_geo.a"; sourceTree = "<group>"; };
+		907D02F915B86F8700575110 /* libosgdb_glsl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_glsl.a; path = "../../../../osg-ios-gles2/lib/libosgdb_glsl.a"; sourceTree = "<group>"; };
+		907D02FA15B86F8700575110 /* libosgdb_gz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_gz.a; path = "../../../../osg-ios-gles2/lib/libosgdb_gz.a"; sourceTree = "<group>"; };
+		907D02FB15B86F8700575110 /* libosgdb_hdr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_hdr.a; path = "../../../../osg-ios-gles2/lib/libosgdb_hdr.a"; sourceTree = "<group>"; };
+		907D02FC15B86F8700575110 /* libosgdb_imageio.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_imageio.a; path = "../../../../osg-ios-gles2/lib/libosgdb_imageio.a"; sourceTree = "<group>"; };
+		907D02FD15B86F8700575110 /* libosgdb_ive.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ive.a; path = "../../../../osg-ios-gles2/lib/libosgdb_ive.a"; sourceTree = "<group>"; };
+		907D02FE15B86F8700575110 /* libosgdb_logo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_logo.a; path = "../../../../osg-ios-gles2/lib/libosgdb_logo.a"; sourceTree = "<group>"; };
+		907D02FF15B86F8700575110 /* libosgdb_lwo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_lwo.a; path = "../../../../osg-ios-gles2/lib/libosgdb_lwo.a"; sourceTree = "<group>"; };
+		907D030015B86F8700575110 /* libosgdb_lws.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_lws.a; path = "../../../../osg-ios-gles2/lib/libosgdb_lws.a"; sourceTree = "<group>"; };
+		907D030115B86F8700575110 /* libosgdb_md2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_md2.a; path = "../../../../osg-ios-gles2/lib/libosgdb_md2.a"; sourceTree = "<group>"; };
+		907D030215B86F8700575110 /* libosgdb_mdl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_mdl.a; path = "../../../../osg-ios-gles2/lib/libosgdb_mdl.a"; sourceTree = "<group>"; };
+		907D030315B86F8700575110 /* libosgdb_normals.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_normals.a; path = "../../../../osg-ios-gles2/lib/libosgdb_normals.a"; sourceTree = "<group>"; };
+		907D030415B86F8700575110 /* libosgdb_obj.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_obj.a; path = "../../../../osg-ios-gles2/lib/libosgdb_obj.a"; sourceTree = "<group>"; };
+		907D030515B86F8700575110 /* libosgdb_ogr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ogr.a; path = "../../../../osg-ios-gles2/lib/libosgdb_ogr.a"; sourceTree = "<group>"; };
+		907D030615B86F8700575110 /* libosgdb_openflight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_openflight.a; path = "../../../../osg-ios-gles2/lib/libosgdb_openflight.a"; sourceTree = "<group>"; };
+		907D030715B86F8700575110 /* libosgdb_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osg.a; path = "../../../../osg-ios-gles2/lib/libosgdb_osg.a"; sourceTree = "<group>"; };
+		907D030815B86F8700575110 /* libosgdb_osga.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osga.a; path = "../../../../osg-ios-gles2/lib/libosgdb_osga.a"; sourceTree = "<group>"; };
+		907D030915B86F8700575110 /* libosgdb_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgshadow.a; path = "../../../../osg-ios-gles2/lib/libosgdb_osgshadow.a"; sourceTree = "<group>"; };
+		907D030A15B86F8700575110 /* libosgdb_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgterrain.a; path = "../../../../osg-ios-gles2/lib/libosgdb_osgterrain.a"; sourceTree = "<group>"; };
+		907D030B15B86F8700575110 /* libosgdb_osgtgz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgtgz.a; path = "../../../../osg-ios-gles2/lib/libosgdb_osgtgz.a"; sourceTree = "<group>"; };
+		907D030C15B86F8700575110 /* libosgdb_osgviewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgviewer.a; path = "../../../../osg-ios-gles2/lib/libosgdb_osgviewer.a"; sourceTree = "<group>"; };
+		907D030D15B86F8700575110 /* libosgdb_p3d.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_p3d.a; path = "../../../../osg-ios-gles2/lib/libosgdb_p3d.a"; sourceTree = "<group>"; };
+		907D030E15B86F8700575110 /* libosgdb_pic.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pic.a; path = "../../../../osg-ios-gles2/lib/libosgdb_pic.a"; sourceTree = "<group>"; };
+		907D030F15B86F8700575110 /* libosgdb_ply.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ply.a; path = "../../../../osg-ios-gles2/lib/libosgdb_ply.a"; sourceTree = "<group>"; };
+		907D031015B86F8700575110 /* libosgdb_pnm.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pnm.a; path = "../../../../osg-ios-gles2/lib/libosgdb_pnm.a"; sourceTree = "<group>"; };
+		907D031115B86F8700575110 /* libosgdb_pov.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pov.a; path = "../../../../osg-ios-gles2/lib/libosgdb_pov.a"; sourceTree = "<group>"; };
+		907D031215B86F8700575110 /* libosgdb_pvr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pvr.a; path = "../../../../osg-ios-gles2/lib/libosgdb_pvr.a"; sourceTree = "<group>"; };
+		907D031315B86F8700575110 /* libosgdb_revisions.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_revisions.a; path = "../../../../osg-ios-gles2/lib/libosgdb_revisions.a"; sourceTree = "<group>"; };
+		907D031415B86F8700575110 /* libosgdb_rgb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_rgb.a; path = "../../../../osg-ios-gles2/lib/libosgdb_rgb.a"; sourceTree = "<group>"; };
+		907D031515B86F8700575110 /* libosgdb_rot.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_rot.a; path = "../../../../osg-ios-gles2/lib/libosgdb_rot.a"; sourceTree = "<group>"; };
+		907D031615B86F8700575110 /* libosgdb_scale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_scale.a; path = "../../../../osg-ios-gles2/lib/libosgdb_scale.a"; sourceTree = "<group>"; };
+		907D031715B86F8700575110 /* libosgdb_serializers_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osg.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osg.a"; sourceTree = "<group>"; };
+		907D031815B86F8700575110 /* libosgdb_serializers_osganimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osganimation.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osganimation.a"; sourceTree = "<group>"; };
+		907D031915B86F8700575110 /* libosgdb_serializers_osgfx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgfx.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgfx.a"; sourceTree = "<group>"; };
+		907D031A15B86F8700575110 /* libosgdb_serializers_osgmanipulator.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgmanipulator.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgmanipulator.a"; sourceTree = "<group>"; };
+		907D031B15B86F8700575110 /* libosgdb_serializers_osgparticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgparticle.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgparticle.a"; sourceTree = "<group>"; };
+		907D031C15B86F8700575110 /* libosgdb_serializers_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgshadow.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgshadow.a"; sourceTree = "<group>"; };
+		907D031D15B86F8700575110 /* libosgdb_serializers_osgsim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgsim.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgsim.a"; sourceTree = "<group>"; };
+		907D031E15B86F8700575110 /* libosgdb_serializers_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgterrain.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgterrain.a"; sourceTree = "<group>"; };
+		907D031F15B86F8700575110 /* libosgdb_serializers_osgtext.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgtext.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgtext.a"; sourceTree = "<group>"; };
+		907D032015B86F8700575110 /* libosgdb_serializers_osgvolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgvolume.a; path = "../../../../osg-ios-gles2/lib/libosgdb_serializers_osgvolume.a"; sourceTree = "<group>"; };
+		907D032115B86F8700575110 /* libosgdb_shp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_shp.a; path = "../../../../osg-ios-gles2/lib/libosgdb_shp.a"; sourceTree = "<group>"; };
+		907D032215B86F8700575110 /* libosgdb_stl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_stl.a; path = "../../../../osg-ios-gles2/lib/libosgdb_stl.a"; sourceTree = "<group>"; };
+		907D032315B86F8700575110 /* libosgdb_tga.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tga.a; path = "../../../../osg-ios-gles2/lib/libosgdb_tga.a"; sourceTree = "<group>"; };
+		907D032415B86F8700575110 /* libosgdb_tgz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tgz.a; path = "../../../../osg-ios-gles2/lib/libosgdb_tgz.a"; sourceTree = "<group>"; };
+		907D032515B86F8700575110 /* libosgdb_trans.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_trans.a; path = "../../../../osg-ios-gles2/lib/libosgdb_trans.a"; sourceTree = "<group>"; };
+		907D032615B86F8700575110 /* libosgdb_txf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_txf.a; path = "../../../../osg-ios-gles2/lib/libosgdb_txf.a"; sourceTree = "<group>"; };
+		907D032715B86F8700575110 /* libosgdb_txp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_txp.a; path = "../../../../osg-ios-gles2/lib/libosgdb_txp.a"; sourceTree = "<group>"; };
+		907D032815B86F8700575110 /* libosgdb_vtf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_vtf.a; path = "../../../../osg-ios-gles2/lib/libosgdb_vtf.a"; sourceTree = "<group>"; };
+		907D032915B86F8700575110 /* libosgdb_x.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_x.a; path = "../../../../osg-ios-gles2/lib/libosgdb_x.a"; sourceTree = "<group>"; };
+		907D032A15B86F8700575110 /* libosgdb_zip.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_zip.a; path = "../../../../osg-ios-gles2/lib/libosgdb_zip.a"; sourceTree = "<group>"; };
+		907D032B15B86F8700575110 /* libosgDB.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgDB.a; path = "../../../../osg-ios-gles2/lib/libosgDB.a"; sourceTree = "<group>"; };
+		907D032C15B86F8700575110 /* libosgFX.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgFX.a; path = "../../../../osg-ios-gles2/lib/libosgFX.a"; sourceTree = "<group>"; };
+		907D032D15B86F8700575110 /* libosgGA.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgGA.a; path = "../../../../osg-ios-gles2/lib/libosgGA.a"; sourceTree = "<group>"; };
+		907D032E15B86F8700575110 /* libosgManipulator.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgManipulator.a; path = "../../../../osg-ios-gles2/lib/libosgManipulator.a"; sourceTree = "<group>"; };
+		907D032F15B86F8700575110 /* libosgParticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgParticle.a; path = "../../../../osg-ios-gles2/lib/libosgParticle.a"; sourceTree = "<group>"; };
+		907D033015B86F8700575110 /* libosgPresentation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgPresentation.a; path = "../../../../osg-ios-gles2/lib/libosgPresentation.a"; sourceTree = "<group>"; };
+		907D033115B86F8700575110 /* libosgShadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgShadow.a; path = "../../../../osg-ios-gles2/lib/libosgShadow.a"; sourceTree = "<group>"; };
+		907D033215B86F8700575110 /* libosgSim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgSim.a; path = "../../../../osg-ios-gles2/lib/libosgSim.a"; sourceTree = "<group>"; };
+		907D033315B86F8700575110 /* libosgTerrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgTerrain.a; path = "../../../../osg-ios-gles2/lib/libosgTerrain.a"; sourceTree = "<group>"; };
+		907D033415B86F8700575110 /* libosgText.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgText.a; path = "../../../../osg-ios-gles2/lib/libosgText.a"; sourceTree = "<group>"; };
+		907D033515B86F8700575110 /* libosgUtil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgUtil.a; path = "../../../../osg-ios-gles2/lib/libosgUtil.a"; sourceTree = "<group>"; };
+		907D033615B86F8700575110 /* libosgViewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgViewer.a; path = "../../../../osg-ios-gles2/lib/libosgViewer.a"; sourceTree = "<group>"; };
+		907D033715B86F8700575110 /* libosgVolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgVolume.a; path = "../../../../osg-ios-gles2/lib/libosgVolume.a"; sourceTree = "<group>"; };
+		907D033815B86F8700575110 /* libosgWidget.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgWidget.a; path = "../../../../osg-ios-gles2/lib/libosgWidget.a"; sourceTree = "<group>"; };
+		907D039715B86F9E00575110 /* libosgdb_kml.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_kml.a; path = ../../../lib/Release/libosgdb_kml.a; sourceTree = "<group>"; };
+		907D039815B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_feature_wfs.a; path = ../../../lib/Release/libosgdb_osgearth_feature_wfs.a; sourceTree = "<group>"; };
+		907D039915B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_feature_tfs.a; path = ../../../lib/Release/libosgdb_osgearth_feature_tfs.a; sourceTree = "<group>"; };
+		907D039A15B86F9E00575110 /* libosgdb_osgearth_tms.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_tms.a; path = ../../../lib/Release/libosgdb_osgearth_tms.a; sourceTree = "<group>"; };
+		907D039B15B86F9E00575110 /* libosgdb_osgearth_wms.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_wms.a; path = ../../../lib/Release/libosgdb_osgearth_wms.a; sourceTree = "<group>"; };
+		907D039C15B86F9E00575110 /* libosgdb_osgearth_label_overlay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_label_overlay.a; path = ../../../lib/Release/libosgdb_osgearth_label_overlay.a; sourceTree = "<group>"; };
+		907D039D15B86F9E00575110 /* libosgdb_osgearth_xyz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_xyz.a; path = ../../../lib/Release/libosgdb_osgearth_xyz.a; sourceTree = "<group>"; };
+		907D039E15B86F9E00575110 /* libosgEarthUtil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthUtil.a; path = ../../../lib/Release/libosgEarthUtil.a; sourceTree = "<group>"; };
+		907D039F15B86F9E00575110 /* libosgdb_osgearth_label_annotation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_label_annotation.a; path = ../../../lib/Release/libosgdb_osgearth_label_annotation.a; sourceTree = "<group>"; };
+		907D03A015B86F9E00575110 /* libosgdb_osgearth_mask_feature.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_mask_feature.a; path = ../../../lib/Release/libosgdb_osgearth_mask_feature.a; sourceTree = "<group>"; };
+		907D03A115B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_model_feature_geom.a; path = ../../../lib/Release/libosgdb_osgearth_model_feature_geom.a; sourceTree = "<group>"; };
+		907D03A215B86F9E00575110 /* libosgEarthAnnotation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthAnnotation.a; path = ../../../lib/Release/libosgEarthAnnotation.a; sourceTree = "<group>"; };
+		907D03A315B86F9E00575110 /* libosgdb_osgearth_agglite.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_agglite.a; path = ../../../lib/Release/libosgdb_osgearth_agglite.a; sourceTree = "<group>"; };
+		907D03A415B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_feature_ogr.a; path = ../../../lib/Release/libosgdb_osgearth_feature_ogr.a; sourceTree = "<group>"; };
+		907D03A515B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_model_feature_stencil.a; path = ../../../lib/Release/libosgdb_osgearth_model_feature_stencil.a; sourceTree = "<group>"; };
+		907D03A615B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vdatum_egm2008.a; path = ../../../lib/Release/libosgdb_osgearth_vdatum_egm2008.a; sourceTree = "<group>"; };
+		907D03A715B86F9E00575110 /* libosgdb_osgearth_model_simple.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_model_simple.a; path = ../../../lib/Release/libosgdb_osgearth_model_simple.a; sourceTree = "<group>"; };
+		907D03A815B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_engine_osgterrain.a; path = ../../../lib/Release/libosgdb_osgearth_engine_osgterrain.a; sourceTree = "<group>"; };
+		907D03A915B86F9E00575110 /* libosgEarthFeatures.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthFeatures.a; path = ../../../lib/Release/libosgEarthFeatures.a; sourceTree = "<group>"; };
+		907D03AA15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vdatum_egm96.a; path = ../../../lib/Release/libosgdb_osgearth_vdatum_egm96.a; sourceTree = "<group>"; };
+		907D03AB15B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_ocean_surface.a; path = ../../../lib/Release/libosgdb_osgearth_ocean_surface.a; sourceTree = "<group>"; };
+		907D03AC15B86F9E00575110 /* libosgdb_osgearth_debug.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_debug.a; path = ../../../lib/Release/libosgdb_osgearth_debug.a; sourceTree = "<group>"; };
+		907D03AD15B86F9E00575110 /* libosgdb_osgearth_mbtiles.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_mbtiles.a; path = ../../../lib/Release/libosgdb_osgearth_mbtiles.a; sourceTree = "<group>"; };
+		907D03AE15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vdatum_egm84.a; path = ../../../lib/Release/libosgdb_osgearth_vdatum_egm84.a; sourceTree = "<group>"; };
+		907D03AF15B86F9E00575110 /* libosgdb_osgearth_tileservice.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_tileservice.a; path = ../../../lib/Release/libosgdb_osgearth_tileservice.a; sourceTree = "<group>"; };
+		907D03B015B86F9E00575110 /* libosgdb_osgearth_yahoo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_yahoo.a; path = ../../../lib/Release/libosgdb_osgearth_yahoo.a; sourceTree = "<group>"; };
+		907D03B115B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_arcgis_map_cache.a; path = ../../../lib/Release/libosgdb_osgearth_arcgis_map_cache.a; sourceTree = "<group>"; };
+		907D03B215B86F9E00575110 /* libosgdb_osgearth_tilecache.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_tilecache.a; path = ../../../lib/Release/libosgdb_osgearth_tilecache.a; sourceTree = "<group>"; };
+		907D03B315B86F9E00575110 /* libosgdb_osgearth_wcs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_wcs.a; path = ../../../lib/Release/libosgdb_osgearth_wcs.a; sourceTree = "<group>"; };
+		907D03B415B86F9E00575110 /* libosgEarthSymbology.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthSymbology.a; path = ../../../lib/Release/libosgEarthSymbology.a; sourceTree = "<group>"; };
+		907D03B515B86F9E00575110 /* libosgdb_osgearth_gdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_gdal.a; path = ../../../lib/Release/libosgdb_osgearth_gdal.a; sourceTree = "<group>"; };
+		907D03B615B86F9E00575110 /* libosgdb_osgearth_refresh.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_refresh.a; path = ../../../lib/Release/libosgdb_osgearth_refresh.a; sourceTree = "<group>"; };
+		907D03B715B86F9E00575110 /* libosgdb_osgearth_vpb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vpb.a; path = ../../../lib/Release/libosgdb_osgearth_vpb.a; sourceTree = "<group>"; };
+		907D03B815B86F9E00575110 /* libosgdb_earth.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_earth.a; path = ../../../lib/Release/libosgdb_earth.a; sourceTree = "<group>"; };
+		907D03B915B86F9E00575110 /* libosgdb_osgearth_cache_filesystem.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_cache_filesystem.a; path = ../../../lib/Release/libosgdb_osgearth_cache_filesystem.a; sourceTree = "<group>"; };
+		907D03BA15B86F9E00575110 /* libosgdb_osgearth_arcgis.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_arcgis.a; path = ../../../lib/Release/libosgdb_osgearth_arcgis.a; sourceTree = "<group>"; };
+		907D03BB15B86F9E00575110 /* libosgdb_osgearth_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_osg.a; path = ../../../lib/Release/libosgdb_osgearth_osg.a; sourceTree = "<group>"; };
+		907D03BC15B86F9E00575110 /* libosgEarth.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarth.a; path = ../../../lib/Release/libosgEarth.a; sourceTree = "<group>"; };
+		907D03F915B8C31A00575110 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
+		907D0A8D15B8CEBE00575110 /* libGEOS_3.2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libGEOS_3.2.a; path = "../../../../3rdParty/geos-ios-device/lib/libGEOS_3.2.a"; sourceTree = "<group>"; };
+		907D0A8F15B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GLES2ShaderGenVisitor.cpp; path = ShaderGen/GLES2ShaderGenVisitor.cpp; sourceTree = "<group>"; };
+		907D0A9015B8DDAA00575110 /* GLES2ShaderGenVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GLES2ShaderGenVisitor.h; path = ShaderGen/GLES2ShaderGenVisitor.h; sourceTree = "<group>"; };
+		90A0DD6C15B7BAF9004FACEE /* libFreeType_iphone_universal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libFreeType_iphone_universal.a; sourceTree = "<group>"; };
+		90A0DD6E15B7BB50004FACEE /* libproj.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libproj.a; sourceTree = "<group>"; };
+		90A0DD7015B7BB64004FACEE /* libgdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgdal.a; sourceTree = "<group>"; };
+		90A0DD7515B7BBA4004FACEE /* libcurl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libcurl.a; sourceTree = "<group>"; };
+		90B8676315C8894900F5CDC3 /* StartViewerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StartViewerController.h; sourceTree = "<group>"; };
+		90B8676415C8894900F5CDC3 /* StartViewerController.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = StartViewerController.m; sourceTree = "<group>"; };
+		90B8676515C8894900F5CDC3 /* StartViewerController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StartViewerController.xib; sourceTree = "<group>"; };
+		90DABDDB15CEFF9700D0F609 /* moon_1024x512.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = moon_1024x512.jpg; path = ../../../../data/moon_1024x512.jpg; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		9051FFF715B1EDFD00D9ABD3 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				907D037E15B86F8700575110 /* libosgdb_shp.a in Frameworks */,
+				9048FB2415FA9DE50012C900 /* libosgdb_tiff.a in Frameworks */,
+				907D03FA15B8C31A00575110 /* libsqlite3.dylib in Frameworks */,
+				90A0DD7615B7BBA4004FACEE /* libcurl.a in Frameworks */,
+				90A0DD7115B7BB64004FACEE /* libgdal.a in Frameworks */,
+				90A0DD6F15B7BB50004FACEE /* libproj.a in Frameworks */,
+				90A0DD6D15B7BAF9004FACEE /* libFreeType_iphone_universal.a in Frameworks */,
+				905100D115B2185000D9ABD3 /* libiconv.2.4.0.dylib in Frameworks */,
+				905100CF15B217B500D9ABD3 /* libz.1.1.3.dylib in Frameworks */,
+				905100CD15B217A800D9ABD3 /* libc++.dylib in Frameworks */,
+				905100CB15B2101E00D9ABD3 /* QuartzCore.framework in Frameworks */,
+				905100C515B20AD100D9ABD3 /* CoreImage.framework in Frameworks */,
+				905100C315B20AC600D9ABD3 /* ImageIO.framework in Frameworks */,
+				9051FFFF15B1EDFD00D9ABD3 /* UIKit.framework in Frameworks */,
+				9051000115B1EDFD00D9ABD3 /* Foundation.framework in Frameworks */,
+				9051000315B1EDFD00D9ABD3 /* CoreGraphics.framework in Frameworks */,
+				9051000715B1EDFD00D9ABD3 /* OpenGLES.framework in Frameworks */,
+				907D033915B86F8700575110 /* libOpenThreads.a in Frameworks */,
+				907D033A15B86F8700575110 /* libosg.a in Frameworks */,
+				907D033B15B86F8700575110 /* libosgAnimation.a in Frameworks */,
+				907D033C15B86F8700575110 /* libosgdb_3dc.a in Frameworks */,
+				907D033D15B86F8700575110 /* libosgdb_3ds.a in Frameworks */,
+				907D033E15B86F8700575110 /* libosgdb_ac.a in Frameworks */,
+				907D033F15B86F8700575110 /* libosgdb_bmp.a in Frameworks */,
+				907D034015B86F8700575110 /* libosgdb_bsp.a in Frameworks */,
+				907D034115B86F8700575110 /* libosgdb_bvh.a in Frameworks */,
+				907D034215B86F8700575110 /* libosgdb_cfg.a in Frameworks */,
+				907D034315B86F8700575110 /* libosgdb_curl.a in Frameworks */,
+				907D034415B86F8700575110 /* libosgdb_dds.a in Frameworks */,
+				907D034515B86F8700575110 /* libosgdb_deprecated_osg.a in Frameworks */,
+				907D034615B86F8700575110 /* libosgdb_deprecated_osganimation.a in Frameworks */,
+				907D034715B86F8700575110 /* libosgdb_deprecated_osgfx.a in Frameworks */,
+				907D034815B86F8700575110 /* libosgdb_deprecated_osgparticle.a in Frameworks */,
+				907D034915B86F8700575110 /* libosgdb_deprecated_osgshadow.a in Frameworks */,
+				907D034A15B86F8700575110 /* libosgdb_deprecated_osgsim.a in Frameworks */,
+				907D034B15B86F8700575110 /* libosgdb_deprecated_osgterrain.a in Frameworks */,
+				907D034C15B86F8700575110 /* libosgdb_deprecated_osgtext.a in Frameworks */,
+				907D034D15B86F8700575110 /* libosgdb_deprecated_osgviewer.a in Frameworks */,
+				907D034E15B86F8700575110 /* libosgdb_deprecated_osgvolume.a in Frameworks */,
+				907D034F15B86F8700575110 /* libosgdb_deprecated_osgwidget.a in Frameworks */,
+				907D035015B86F8700575110 /* libosgdb_dot.a in Frameworks */,
+				907D035115B86F8700575110 /* libosgdb_dw.a in Frameworks */,
+				907D035215B86F8700575110 /* libosgdb_dxf.a in Frameworks */,
+				907D035315B86F8700575110 /* libosgdb_freetype.a in Frameworks */,
+				907D035415B86F8700575110 /* libosgdb_gdal.a in Frameworks */,
+				907D035515B86F8700575110 /* libosgdb_geo.a in Frameworks */,
+				907D035615B86F8700575110 /* libosgdb_glsl.a in Frameworks */,
+				907D035715B86F8700575110 /* libosgdb_gz.a in Frameworks */,
+				907D035815B86F8700575110 /* libosgdb_hdr.a in Frameworks */,
+				907D035915B86F8700575110 /* libosgdb_imageio.a in Frameworks */,
+				907D035A15B86F8700575110 /* libosgdb_ive.a in Frameworks */,
+				907D035B15B86F8700575110 /* libosgdb_logo.a in Frameworks */,
+				907D035C15B86F8700575110 /* libosgdb_lwo.a in Frameworks */,
+				907D035D15B86F8700575110 /* libosgdb_lws.a in Frameworks */,
+				907D035E15B86F8700575110 /* libosgdb_md2.a in Frameworks */,
+				907D035F15B86F8700575110 /* libosgdb_mdl.a in Frameworks */,
+				907D036015B86F8700575110 /* libosgdb_normals.a in Frameworks */,
+				907D036115B86F8700575110 /* libosgdb_obj.a in Frameworks */,
+				907D036215B86F8700575110 /* libosgdb_ogr.a in Frameworks */,
+				907D036315B86F8700575110 /* libosgdb_openflight.a in Frameworks */,
+				907D036415B86F8700575110 /* libosgdb_osg.a in Frameworks */,
+				907D036515B86F8700575110 /* libosgdb_osga.a in Frameworks */,
+				907D036615B86F8700575110 /* libosgdb_osgshadow.a in Frameworks */,
+				907D036715B86F8700575110 /* libosgdb_osgterrain.a in Frameworks */,
+				907D036815B86F8700575110 /* libosgdb_osgtgz.a in Frameworks */,
+				907D036915B86F8700575110 /* libosgdb_osgviewer.a in Frameworks */,
+				907D036A15B86F8700575110 /* libosgdb_p3d.a in Frameworks */,
+				907D036B15B86F8700575110 /* libosgdb_pic.a in Frameworks */,
+				907D036C15B86F8700575110 /* libosgdb_ply.a in Frameworks */,
+				907D036D15B86F8700575110 /* libosgdb_pnm.a in Frameworks */,
+				907D036E15B86F8700575110 /* libosgdb_pov.a in Frameworks */,
+				907D036F15B86F8700575110 /* libosgdb_pvr.a in Frameworks */,
+				907D037015B86F8700575110 /* libosgdb_revisions.a in Frameworks */,
+				907D037115B86F8700575110 /* libosgdb_rgb.a in Frameworks */,
+				907D037215B86F8700575110 /* libosgdb_rot.a in Frameworks */,
+				907D037315B86F8700575110 /* libosgdb_scale.a in Frameworks */,
+				907D037415B86F8700575110 /* libosgdb_serializers_osg.a in Frameworks */,
+				907D037515B86F8700575110 /* libosgdb_serializers_osganimation.a in Frameworks */,
+				907D037615B86F8700575110 /* libosgdb_serializers_osgfx.a in Frameworks */,
+				907D037715B86F8700575110 /* libosgdb_serializers_osgmanipulator.a in Frameworks */,
+				907D037815B86F8700575110 /* libosgdb_serializers_osgparticle.a in Frameworks */,
+				907D037915B86F8700575110 /* libosgdb_serializers_osgshadow.a in Frameworks */,
+				907D037A15B86F8700575110 /* libosgdb_serializers_osgsim.a in Frameworks */,
+				907D037B15B86F8700575110 /* libosgdb_serializers_osgterrain.a in Frameworks */,
+				907D037C15B86F8700575110 /* libosgdb_serializers_osgtext.a in Frameworks */,
+				907D037D15B86F8700575110 /* libosgdb_serializers_osgvolume.a in Frameworks */,
+				907D037F15B86F8700575110 /* libosgdb_stl.a in Frameworks */,
+				907D038015B86F8700575110 /* libosgdb_tga.a in Frameworks */,
+				907D038115B86F8700575110 /* libosgdb_tgz.a in Frameworks */,
+				907D038215B86F8700575110 /* libosgdb_trans.a in Frameworks */,
+				907D038315B86F8700575110 /* libosgdb_txf.a in Frameworks */,
+				907D038415B86F8700575110 /* libosgdb_txp.a in Frameworks */,
+				907D038515B86F8700575110 /* libosgdb_vtf.a in Frameworks */,
+				907D038615B86F8700575110 /* libosgdb_x.a in Frameworks */,
+				907D038715B86F8700575110 /* libosgdb_zip.a in Frameworks */,
+				907D038815B86F8700575110 /* libosgDB.a in Frameworks */,
+				907D038915B86F8700575110 /* libosgFX.a in Frameworks */,
+				907D038A15B86F8700575110 /* libosgGA.a in Frameworks */,
+				907D038B15B86F8700575110 /* libosgManipulator.a in Frameworks */,
+				907D038C15B86F8700575110 /* libosgParticle.a in Frameworks */,
+				907D038D15B86F8700575110 /* libosgPresentation.a in Frameworks */,
+				907D038E15B86F8700575110 /* libosgShadow.a in Frameworks */,
+				907D038F15B86F8700575110 /* libosgSim.a in Frameworks */,
+				907D039015B86F8700575110 /* libosgTerrain.a in Frameworks */,
+				907D039115B86F8700575110 /* libosgText.a in Frameworks */,
+				907D039215B86F8700575110 /* libosgUtil.a in Frameworks */,
+				907D039315B86F8700575110 /* libosgViewer.a in Frameworks */,
+				907D039415B86F8700575110 /* libosgVolume.a in Frameworks */,
+				907D039515B86F8700575110 /* libosgWidget.a in Frameworks */,
+				907D03BD15B86F9E00575110 /* libosgdb_kml.a in Frameworks */,
+				907D03BE15B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a in Frameworks */,
+				907D03BF15B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a in Frameworks */,
+				907D03C015B86F9E00575110 /* libosgdb_osgearth_tms.a in Frameworks */,
+				907D03C115B86F9E00575110 /* libosgdb_osgearth_wms.a in Frameworks */,
+				907D03C215B86F9E00575110 /* libosgdb_osgearth_label_overlay.a in Frameworks */,
+				907D03C315B86F9E00575110 /* libosgdb_osgearth_xyz.a in Frameworks */,
+				907D03C415B86F9E00575110 /* libosgEarthUtil.a in Frameworks */,
+				907D03C515B86F9E00575110 /* libosgdb_osgearth_label_annotation.a in Frameworks */,
+				907D03C615B86F9E00575110 /* libosgdb_osgearth_mask_feature.a in Frameworks */,
+				907D03C715B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a in Frameworks */,
+				907D03C815B86F9E00575110 /* libosgEarthAnnotation.a in Frameworks */,
+				907D03C915B86F9E00575110 /* libosgdb_osgearth_agglite.a in Frameworks */,
+				907D03CA15B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a in Frameworks */,
+				907D03CB15B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a in Frameworks */,
+				907D03CC15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a in Frameworks */,
+				907D03CD15B86F9E00575110 /* libosgdb_osgearth_model_simple.a in Frameworks */,
+				907D03CE15B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a in Frameworks */,
+				907D03CF15B86F9E00575110 /* libosgEarthFeatures.a in Frameworks */,
+				907D03D015B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a in Frameworks */,
+				907D03D115B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a in Frameworks */,
+				907D03D215B86F9E00575110 /* libosgdb_osgearth_debug.a in Frameworks */,
+				907D03D315B86F9E00575110 /* libosgdb_osgearth_mbtiles.a in Frameworks */,
+				907D03D415B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a in Frameworks */,
+				907D03D515B86F9E00575110 /* libosgdb_osgearth_tileservice.a in Frameworks */,
+				907D03D615B86F9E00575110 /* libosgdb_osgearth_yahoo.a in Frameworks */,
+				907D03D715B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a in Frameworks */,
+				907D03D815B86F9E00575110 /* libosgdb_osgearth_tilecache.a in Frameworks */,
+				907D03D915B86F9E00575110 /* libosgdb_osgearth_wcs.a in Frameworks */,
+				907D03DA15B86F9E00575110 /* libosgEarthSymbology.a in Frameworks */,
+				907D03DB15B86F9F00575110 /* libosgdb_osgearth_gdal.a in Frameworks */,
+				907D03DC15B86F9F00575110 /* libosgdb_osgearth_refresh.a in Frameworks */,
+				907D03DD15B86F9F00575110 /* libosgdb_osgearth_vpb.a in Frameworks */,
+				907D03DE15B86F9F00575110 /* libosgdb_earth.a in Frameworks */,
+				907D03DF15B86F9F00575110 /* libosgdb_osgearth_cache_filesystem.a in Frameworks */,
+				907D03E015B86F9F00575110 /* libosgdb_osgearth_arcgis.a in Frameworks */,
+				907D03E115B86F9F00575110 /* libosgdb_osgearth_osg.a in Frameworks */,
+				907D03E215B86F9F00575110 /* libosgEarth.a in Frameworks */,
+				907D0A8E15B8CEBE00575110 /* libGEOS_3.2.a in Frameworks */,
+				90283E5D15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		9051000815B1EDFD00D9ABD3 /* osgEarthViewerIOS */ = {
+			isa = PBXGroup;
+			children = (
+				90B8676915C8895100F5CDC3 /* StartView */,
+				908C793D15BB0B4E001CFA5E /* MultiTouchManipulator */,
+				907D0A9215B8DDAF00575110 /* ShaderGen */,
+				905100C615B20B1D00D9ABD3 /* osgPlugins.h */,
+				9051001115B1EDFD00D9ABD3 /* AppDelegate.h */,
+				9051001215B1EDFD00D9ABD3 /* AppDelegate.m */,
+				9051001815B1EDFD00D9ABD3 /* ViewController.h */,
+				9051001915B1EDFD00D9ABD3 /* ViewController.m */,
+				9051001B15B1EDFD00D9ABD3 /* ViewController_iPhone.xib */,
+				9051001E15B1EDFD00D9ABD3 /* ViewController_iPad.xib */,
+				9051000915B1EDFD00D9ABD3 /* Supporting Files */,
+			);
+			path = osgEarthViewerIOS;
+			sourceTree = "<group>";
+		};
+		9051000915B1EDFD00D9ABD3 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				9051000A15B1EDFD00D9ABD3 /* osgEarthViewerIOS-Info.plist */,
+				9051000B15B1EDFD00D9ABD3 /* InfoPlist.strings */,
+				9051000E15B1EDFD00D9ABD3 /* main.m */,
+				9051001015B1EDFD00D9ABD3 /* osgEarthViewerIOS-Prefix.pch */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		905100D215B21FFD00D9ABD3 /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				90DABDDB15CEFF9700D0F609 /* moon_1024x512.jpg */,
+				90283E5415C6FB3E00620EEF /* tests */,
+				90283E5515C6FB3E00620EEF /* data */,
+			);
+			name = Resources;
+			path = osgEarthViewerIOS;
+			sourceTree = "<group>";
+		};
+		9051FFEF15B1EDFD00D9ABD3 = {
+			isa = PBXGroup;
+			children = (
+				9051000815B1EDFD00D9ABD3 /* osgEarthViewerIOS */,
+				905100D215B21FFD00D9ABD3 /* Resources */,
+				90A0DD7715B7BBAB004FACEE /* Libs */,
+				9051FFFB15B1EDFD00D9ABD3 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		9051FFFB15B1EDFD00D9ABD3 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				9051FFFA15B1EDFD00D9ABD3 /* osgEarth.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		9051FFFD15B1EDFD00D9ABD3 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				905100CA15B2101E00D9ABD3 /* QuartzCore.framework */,
+				905100C415B20AD100D9ABD3 /* CoreImage.framework */,
+				905100C215B20AC600D9ABD3 /* ImageIO.framework */,
+				9051FFFE15B1EDFD00D9ABD3 /* UIKit.framework */,
+				9051000015B1EDFD00D9ABD3 /* Foundation.framework */,
+				9051000215B1EDFD00D9ABD3 /* CoreGraphics.framework */,
+				9051000615B1EDFD00D9ABD3 /* OpenGLES.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		907D02DB15B86F7100575110 /* osg */ = {
+			isa = PBXGroup;
+			children = (
+				907D02DC15B86F8700575110 /* libOpenThreads.a */,
+				907D02DD15B86F8700575110 /* libosg.a */,
+				907D02DE15B86F8700575110 /* libosgAnimation.a */,
+				907D02DF15B86F8700575110 /* libosgdb_3dc.a */,
+				907D02E015B86F8700575110 /* libosgdb_3ds.a */,
+				907D02E115B86F8700575110 /* libosgdb_ac.a */,
+				907D02E215B86F8700575110 /* libosgdb_bmp.a */,
+				907D02E315B86F8700575110 /* libosgdb_bsp.a */,
+				907D02E415B86F8700575110 /* libosgdb_bvh.a */,
+				907D02E515B86F8700575110 /* libosgdb_cfg.a */,
+				907D02E615B86F8700575110 /* libosgdb_curl.a */,
+				907D02E715B86F8700575110 /* libosgdb_dds.a */,
+				9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */,
+				907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */,
+				907D02E915B86F8700575110 /* libosgdb_deprecated_osganimation.a */,
+				907D02EA15B86F8700575110 /* libosgdb_deprecated_osgfx.a */,
+				907D02EB15B86F8700575110 /* libosgdb_deprecated_osgparticle.a */,
+				907D02EC15B86F8700575110 /* libosgdb_deprecated_osgshadow.a */,
+				907D02ED15B86F8700575110 /* libosgdb_deprecated_osgsim.a */,
+				907D02EE15B86F8700575110 /* libosgdb_deprecated_osgterrain.a */,
+				907D02EF15B86F8700575110 /* libosgdb_deprecated_osgtext.a */,
+				907D02F015B86F8700575110 /* libosgdb_deprecated_osgviewer.a */,
+				907D02F115B86F8700575110 /* libosgdb_deprecated_osgvolume.a */,
+				907D02F215B86F8700575110 /* libosgdb_deprecated_osgwidget.a */,
+				907D02F315B86F8700575110 /* libosgdb_dot.a */,
+				907D02F415B86F8700575110 /* libosgdb_dw.a */,
+				907D02F515B86F8700575110 /* libosgdb_dxf.a */,
+				907D02F615B86F8700575110 /* libosgdb_freetype.a */,
+				907D02F715B86F8700575110 /* libosgdb_gdal.a */,
+				907D02F815B86F8700575110 /* libosgdb_geo.a */,
+				907D02F915B86F8700575110 /* libosgdb_glsl.a */,
+				907D02FA15B86F8700575110 /* libosgdb_gz.a */,
+				907D02FB15B86F8700575110 /* libosgdb_hdr.a */,
+				907D02FC15B86F8700575110 /* libosgdb_imageio.a */,
+				907D02FD15B86F8700575110 /* libosgdb_ive.a */,
+				907D02FE15B86F8700575110 /* libosgdb_logo.a */,
+				907D02FF15B86F8700575110 /* libosgdb_lwo.a */,
+				907D030015B86F8700575110 /* libosgdb_lws.a */,
+				907D030115B86F8700575110 /* libosgdb_md2.a */,
+				907D030215B86F8700575110 /* libosgdb_mdl.a */,
+				907D030315B86F8700575110 /* libosgdb_normals.a */,
+				907D030415B86F8700575110 /* libosgdb_obj.a */,
+				907D030515B86F8700575110 /* libosgdb_ogr.a */,
+				907D030615B86F8700575110 /* libosgdb_openflight.a */,
+				907D030715B86F8700575110 /* libosgdb_osg.a */,
+				907D030815B86F8700575110 /* libosgdb_osga.a */,
+				907D030915B86F8700575110 /* libosgdb_osgshadow.a */,
+				907D030A15B86F8700575110 /* libosgdb_osgterrain.a */,
+				907D030B15B86F8700575110 /* libosgdb_osgtgz.a */,
+				907D030C15B86F8700575110 /* libosgdb_osgviewer.a */,
+				907D030D15B86F8700575110 /* libosgdb_p3d.a */,
+				907D030E15B86F8700575110 /* libosgdb_pic.a */,
+				907D030F15B86F8700575110 /* libosgdb_ply.a */,
+				907D031015B86F8700575110 /* libosgdb_pnm.a */,
+				907D031115B86F8700575110 /* libosgdb_pov.a */,
+				907D031215B86F8700575110 /* libosgdb_pvr.a */,
+				907D031315B86F8700575110 /* libosgdb_revisions.a */,
+				907D031415B86F8700575110 /* libosgdb_rgb.a */,
+				907D031515B86F8700575110 /* libosgdb_rot.a */,
+				907D031615B86F8700575110 /* libosgdb_scale.a */,
+				907D031715B86F8700575110 /* libosgdb_serializers_osg.a */,
+				907D031815B86F8700575110 /* libosgdb_serializers_osganimation.a */,
+				907D031915B86F8700575110 /* libosgdb_serializers_osgfx.a */,
+				907D031A15B86F8700575110 /* libosgdb_serializers_osgmanipulator.a */,
+				907D031B15B86F8700575110 /* libosgdb_serializers_osgparticle.a */,
+				907D031C15B86F8700575110 /* libosgdb_serializers_osgshadow.a */,
+				907D031D15B86F8700575110 /* libosgdb_serializers_osgsim.a */,
+				907D031E15B86F8700575110 /* libosgdb_serializers_osgterrain.a */,
+				907D031F15B86F8700575110 /* libosgdb_serializers_osgtext.a */,
+				907D032015B86F8700575110 /* libosgdb_serializers_osgvolume.a */,
+				907D032115B86F8700575110 /* libosgdb_shp.a */,
+				907D032215B86F8700575110 /* libosgdb_stl.a */,
+				907D032315B86F8700575110 /* libosgdb_tga.a */,
+				907D032415B86F8700575110 /* libosgdb_tgz.a */,
+				907D032515B86F8700575110 /* libosgdb_trans.a */,
+				907D032615B86F8700575110 /* libosgdb_txf.a */,
+				907D032715B86F8700575110 /* libosgdb_txp.a */,
+				907D032815B86F8700575110 /* libosgdb_vtf.a */,
+				907D032915B86F8700575110 /* libosgdb_x.a */,
+				907D032A15B86F8700575110 /* libosgdb_zip.a */,
+				907D032B15B86F8700575110 /* libosgDB.a */,
+				907D032C15B86F8700575110 /* libosgFX.a */,
+				907D032D15B86F8700575110 /* libosgGA.a */,
+				907D032E15B86F8700575110 /* libosgManipulator.a */,
+				907D032F15B86F8700575110 /* libosgParticle.a */,
+				907D033015B86F8700575110 /* libosgPresentation.a */,
+				907D033115B86F8700575110 /* libosgShadow.a */,
+				907D033215B86F8700575110 /* libosgSim.a */,
+				907D033315B86F8700575110 /* libosgTerrain.a */,
+				907D033415B86F8700575110 /* libosgText.a */,
+				907D033515B86F8700575110 /* libosgUtil.a */,
+				907D033615B86F8700575110 /* libosgViewer.a */,
+				907D033715B86F8700575110 /* libosgVolume.a */,
+				907D033815B86F8700575110 /* libosgWidget.a */,
+			);
+			name = osg;
+			sourceTree = "<group>";
+		};
+		907D039615B86F8C00575110 /* osgEarth */ = {
+			isa = PBXGroup;
+			children = (
+				907D039715B86F9E00575110 /* libosgdb_kml.a */,
+				907D039815B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a */,
+				907D039915B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a */,
+				907D039A15B86F9E00575110 /* libosgdb_osgearth_tms.a */,
+				907D039B15B86F9E00575110 /* libosgdb_osgearth_wms.a */,
+				907D039C15B86F9E00575110 /* libosgdb_osgearth_label_overlay.a */,
+				907D039D15B86F9E00575110 /* libosgdb_osgearth_xyz.a */,
+				907D039E15B86F9E00575110 /* libosgEarthUtil.a */,
+				907D039F15B86F9E00575110 /* libosgdb_osgearth_label_annotation.a */,
+				907D03A015B86F9E00575110 /* libosgdb_osgearth_mask_feature.a */,
+				907D03A115B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a */,
+				907D03A215B86F9E00575110 /* libosgEarthAnnotation.a */,
+				907D03A315B86F9E00575110 /* libosgdb_osgearth_agglite.a */,
+				907D03A415B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a */,
+				907D03A515B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a */,
+				907D03A615B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a */,
+				907D03A715B86F9E00575110 /* libosgdb_osgearth_model_simple.a */,
+				907D03A815B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a */,
+				90283E5C15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a */,
+				907D03A915B86F9E00575110 /* libosgEarthFeatures.a */,
+				907D03AA15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a */,
+				907D03AB15B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a */,
+				907D03AC15B86F9E00575110 /* libosgdb_osgearth_debug.a */,
+				907D03AD15B86F9E00575110 /* libosgdb_osgearth_mbtiles.a */,
+				907D03AE15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a */,
+				907D03AF15B86F9E00575110 /* libosgdb_osgearth_tileservice.a */,
+				907D03B015B86F9E00575110 /* libosgdb_osgearth_yahoo.a */,
+				907D03B115B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a */,
+				907D03B215B86F9E00575110 /* libosgdb_osgearth_tilecache.a */,
+				907D03B315B86F9E00575110 /* libosgdb_osgearth_wcs.a */,
+				907D03B415B86F9E00575110 /* libosgEarthSymbology.a */,
+				907D03B515B86F9E00575110 /* libosgdb_osgearth_gdal.a */,
+				907D03B615B86F9E00575110 /* libosgdb_osgearth_refresh.a */,
+				907D03B715B86F9E00575110 /* libosgdb_osgearth_vpb.a */,
+				907D03B815B86F9E00575110 /* libosgdb_earth.a */,
+				907D03B915B86F9E00575110 /* libosgdb_osgearth_cache_filesystem.a */,
+				907D03BA15B86F9E00575110 /* libosgdb_osgearth_arcgis.a */,
+				907D03BB15B86F9E00575110 /* libosgdb_osgearth_osg.a */,
+				907D03BC15B86F9E00575110 /* libosgEarth.a */,
+			);
+			name = osgEarth;
+			sourceTree = "<group>";
+		};
+		907D0A9215B8DDAF00575110 /* ShaderGen */ = {
+			isa = PBXGroup;
+			children = (
+				907D0A8F15B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp */,
+				907D0A9015B8DDAA00575110 /* GLES2ShaderGenVisitor.h */,
+			);
+			name = ShaderGen;
+			sourceTree = "<group>";
+		};
+		908C793D15BB0B4E001CFA5E /* MultiTouchManipulator */ = {
+			isa = PBXGroup;
+			children = (
+				903B45D915C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp */,
+				903B45DA15C0DE9F00F7702B /* EarthMultiTouchManipulator.h */,
+			);
+			path = MultiTouchManipulator;
+			sourceTree = "<group>";
+		};
+		90A0DD7215B7BB68004FACEE /* gdal */ = {
+			isa = PBXGroup;
+			children = (
+				90A0DD7015B7BB64004FACEE /* libgdal.a */,
+			);
+			name = gdal;
+			path = "../../../../3rdParty/gdal-ios-device/lib";
+			sourceTree = "<group>";
+		};
+		90A0DD7315B7BB72004FACEE /* freetype */ = {
+			isa = PBXGroup;
+			children = (
+				90A0DD6C15B7BAF9004FACEE /* libFreeType_iphone_universal.a */,
+			);
+			name = freetype;
+			path = "../../../../3rdParty/freetype-ios-universal/lib";
+			sourceTree = "<group>";
+		};
+		90A0DD7415B7BB7C004FACEE /* proj4 */ = {
+			isa = PBXGroup;
+			children = (
+				90A0DD6E15B7BB50004FACEE /* libproj.a */,
+			);
+			name = proj4;
+			path = "../../../../3rdParty/proj4-ios-device/lib";
+			sourceTree = "<group>";
+		};
+		90A0DD7715B7BBAB004FACEE /* Libs */ = {
+			isa = PBXGroup;
+			children = (
+				907D0A8D15B8CEBE00575110 /* libGEOS_3.2.a */,
+				907D039615B86F8C00575110 /* osgEarth */,
+				907D02DB15B86F7100575110 /* osg */,
+				90A0DD7815B7BBCE004FACEE /* curl */,
+				90A0DD7415B7BB7C004FACEE /* proj4 */,
+				90A0DD7315B7BB72004FACEE /* freetype */,
+				90A0DD7215B7BB68004FACEE /* gdal */,
+				905100D015B2185000D9ABD3 /* libiconv.2.4.0.dylib */,
+				905100CE15B217B500D9ABD3 /* libz.1.1.3.dylib */,
+				905100CC15B217A800D9ABD3 /* libc++.dylib */,
+				907D03F915B8C31A00575110 /* libsqlite3.dylib */,
+				9051FFFD15B1EDFD00D9ABD3 /* Frameworks */,
+			);
+			name = Libs;
+			sourceTree = "<group>";
+		};
+		90A0DD7815B7BBCE004FACEE /* curl */ = {
+			isa = PBXGroup;
+			children = (
+				90A0DD7515B7BBA4004FACEE /* libcurl.a */,
+			);
+			name = curl;
+			path = "../../../../3rdParty/curl-ios-device/lib";
+			sourceTree = "<group>";
+		};
+		90B8676915C8895100F5CDC3 /* StartView */ = {
+			isa = PBXGroup;
+			children = (
+				90B8676315C8894900F5CDC3 /* StartViewerController.h */,
+				90B8676415C8894900F5CDC3 /* StartViewerController.m */,
+				90B8676515C8894900F5CDC3 /* StartViewerController.xib */,
+			);
+			name = StartView;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		9051FFF915B1EDFD00D9ABD3 /* osgEarthViewerIOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 9051002315B1EDFD00D9ABD3 /* Build configuration list for PBXNativeTarget "osgEarthViewerIOS" */;
+			buildPhases = (
+				9051FFF615B1EDFD00D9ABD3 /* Sources */,
+				9051FFF715B1EDFD00D9ABD3 /* Frameworks */,
+				9051FFF815B1EDFD00D9ABD3 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = osgEarthViewerIOS;
+			productName = osgEarthViewerIOS;
+			productReference = 9051FFFA15B1EDFD00D9ABD3 /* osgEarth.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		9051FFF115B1EDFD00D9ABD3 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0430;
+			};
+			buildConfigurationList = 9051FFF415B1EDFD00D9ABD3 /* Build configuration list for PBXProject "osgEarthViewerIOS" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				English,
+			);
+			mainGroup = 9051FFEF15B1EDFD00D9ABD3;
+			productRefGroup = 9051FFFB15B1EDFD00D9ABD3 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				9051FFF915B1EDFD00D9ABD3 /* osgEarthViewerIOS */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		9051FFF815B1EDFD00D9ABD3 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9051000D15B1EDFD00D9ABD3 /* InfoPlist.strings in Resources */,
+				9051001D15B1EDFD00D9ABD3 /* ViewController_iPhone.xib in Resources */,
+				9051002015B1EDFD00D9ABD3 /* ViewController_iPad.xib in Resources */,
+				90283E5615C6FB3E00620EEF /* tests in Resources */,
+				90283E5715C6FB3E00620EEF /* data in Resources */,
+				90B8676715C8894900F5CDC3 /* StartViewerController.xib in Resources */,
+				90DABDDC15CEFF9700D0F609 /* moon_1024x512.jpg in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		9051FFF615B1EDFD00D9ABD3 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9051000F15B1EDFD00D9ABD3 /* main.m in Sources */,
+				9051001315B1EDFD00D9ABD3 /* AppDelegate.m in Sources */,
+				9051001A15B1EDFD00D9ABD3 /* ViewController.m in Sources */,
+				907D0A9115B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp in Sources */,
+				903B45DB15C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp in Sources */,
+				90B8676615C8894900F5CDC3 /* StartViewerController.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		9051000B15B1EDFD00D9ABD3 /* InfoPlist.strings */ = {
+			isa = PBXVariantGroup;
+			children = (
+				9051000C15B1EDFD00D9ABD3 /* en */,
+			);
+			name = InfoPlist.strings;
+			sourceTree = "<group>";
+		};
+		9051001B15B1EDFD00D9ABD3 /* ViewController_iPhone.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				9051001C15B1EDFD00D9ABD3 /* en */,
+			);
+			name = ViewController_iPhone.xib;
+			sourceTree = "<group>";
+		};
+		9051001E15B1EDFD00D9ABD3 /* ViewController_iPad.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				9051001F15B1EDFD00D9ABD3 /* en */,
+			);
+			name = ViewController_iPad.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		9051002115B1EDFD00D9ABD3 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		9051002215B1EDFD00D9ABD3 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		9051002415B1EDFD00D9ABD3 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Thomas Hogarth (35UD7TG27V)";
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch";
+				"GCC_THUMB_SUPPORT[arch=armv6]" = "";
+				HEADER_SEARCH_PATHS = (
+					/Users/hogbox/Documents/AlphaPixel/osgEarthPort/osgearth/src,
+					"/Users/hogbox/Documents/AlphaPixel/osgEarthPort/osg-ios-gles2/include",
+				);
+				INFOPLIST_FILE = "osgEarthViewerIOS/osgEarthViewerIOS-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+				LIBRARY_SEARCH_PATHS = (
+					"\"$(SRCROOT)/../../../../osg-ios-gles2/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/freetype-ios-universal/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/proj4-ios-device/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/gdal-ios-device/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/curl-ios-device/lib\"",
+					"\"$(SRCROOT)/../../../lib/Release\"",
+					"\"$(SRCROOT)/../../../../3rdParty/geos-ios-device/lib\"",
+				);
+				PRODUCT_NAME = osgEarth;
+				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "C5A8E349-91DB-4E98-9612-789DB499534E";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Debug;
+		};
+		9051002515B1EDFD00D9ABD3 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Thomas Hogarth (35UD7TG27V)";
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch";
+				"GCC_THUMB_SUPPORT[arch=armv6]" = "";
+				HEADER_SEARCH_PATHS = (
+					/Users/hogbox/Documents/AlphaPixel/osgEarthPort/osgearth/src,
+					"/Users/hogbox/Documents/AlphaPixel/osgEarthPort/osg-ios-gles2/include",
+				);
+				INFOPLIST_FILE = "osgEarthViewerIOS/osgEarthViewerIOS-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+				LIBRARY_SEARCH_PATHS = (
+					"\"$(SRCROOT)/../../../../osg-ios-gles2/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/freetype-ios-universal/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/proj4-ios-device/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/gdal-ios-device/lib\"",
+					"\"$(SRCROOT)/../../../../3rdParty/curl-ios-device/lib\"",
+					"\"$(SRCROOT)/../../../lib/Release\"",
+					"\"$(SRCROOT)/../../../../3rdParty/geos-ios-device/lib\"",
+				);
+				PRODUCT_NAME = osgEarth;
+				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "C5A8E349-91DB-4E98-9612-789DB499534E";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		9051002315B1EDFD00D9ABD3 /* Build configuration list for PBXNativeTarget "osgEarthViewerIOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9051002415B1EDFD00D9ABD3 /* Debug */,
+				9051002515B1EDFD00D9ABD3 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		9051FFF415B1EDFD00D9ABD3 /* Build configuration list for PBXProject "osgEarthViewerIOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9051002115B1EDFD00D9ABD3 /* Debug */,
+				9051002215B1EDFD00D9ABD3 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 9051FFF115B1EDFD00D9ABD3 /* Project object */;
+}
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..0901c6b
--- /dev/null
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:osgEarthViewerIOS.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
index fe08880..a30011f 100644
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
@@ -175,8 +175,8 @@ public:
     osgEarth::Util::SkyNode* sky = new osgEarth::Util::SkyNode( mapNode->getMap() );
     sky->setAmbientBrightness( ambientBrightness );
     sky->setDateTime( 1984, 11, 8, hours );
-    sky->attach( _viewer, 0 );
-    root->addChild( sky );
+    //sky->attach( _viewer, 0 );
+    //root->addChild( sky );
     
     
     //for some reason we have to do this as global stateset doesn't
@@ -185,7 +185,7 @@ public:
     
     
     //add model
-     unsigned int numObjects = 2;
+    /*unsigned int numObjects = 2;
     osg::Group* treeGroup = new osg::Group();
     root->addChild(treeGroup);
     osg::Node* tree = osgDB::readNodeFile("./data/boxman.osg");         
@@ -212,16 +212,16 @@ public:
         locator->addChild( mt );
         treeGroup->addChild( locator );
         mapNode->getTerrain()->addTerrainCallback( new ClampObjectLocatorCallback(locator) );        
-    }    
+    }*/    
     
     //manip->setHomeViewpoint(Viewpoint( "Mt Rainier",        osg::Vec3d(    centerLon,   centerLat, 0.0 ), 0.0, -90, 45000 ));
     
     //attach a UpdateLightingUniformsHelper to the model
     UpdateLightingUniformsHelper* updateLightInfo = new UpdateLightingUniformsHelper();
-    treeGroup->setCullCallback(updateLightInfo);
+    //treeGroup->setCullCallback(updateLightInfo);
     
     osgUtil::GLES2ShaderGenVisitor shaderGen;
-    treeGroup->accept(shaderGen);
+    //treeGroup->accept(shaderGen);
     
     _viewer->setSceneData( root );
 }
@@ -250,10 +250,6 @@ public:
     //create the viewer
 	_viewer = new osgViewer::Viewer();
     
-    //_viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
-    _viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(0);
-    _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true,true);
-    
 	// Setup the traits parameters
 	traits->x = 0;
 	traits->y = 0;
@@ -263,7 +259,7 @@ public:
 	traits->alpha = 8;
     //traits->samples = 4;
     //traits->sampleBuffers = 2;
-	//traits->stencil = 1;
+	traits->stencil = 1;
 	traits->windowDecoration = false;
 	traits->doubleBuffer = true;
 	traits->sharedContext = 0;
@@ -281,8 +277,9 @@ public:
     }
     
     _viewer->getCamera()->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
-    _viewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    _viewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
     _viewer->getCamera()->setClearColor(osg::Vec4(1.0f,0.0f,0.0f,0.0f));
+    _viewer->getCamera()->setClearStencil(0);
     _viewer->getCamera()->setProjectionMatrixAsPerspective(45.0f,(float)w/h,
                                                            0.1f, 10000.0f);
 
@@ -293,9 +290,15 @@ public:
     // configure the near/far so we don't clip things that are up close
     _viewer->getCamera()->setNearFarRatio(0.00002);
     
+    //optimize viewer and db pager
+    _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
+    _viewer->getCamera()->setLODScale(_viewer->getCamera()->getLODScale()/2.0);
+    
     // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of
     // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks.
     _viewer->getDatabasePager()->setDoPreCompile( true );
+    _viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(0);
+    _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true,true);
 
   
     _isAnimating=false;
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
index 486ab1a..5329526 100755
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
@@ -17,6 +17,7 @@ USE_GRAPICSWINDOW_IMPLEMENTATION(IOS)
 
 USE_OSGPLUGIN(OpenFlight)
 USE_OSGPLUGIN(obj)
+USE_OSGPLUGIN(shp)
 USE_OSGPLUGIN(ive)
 
 //depreceated osg format
diff --git a/src/osgEarth/CMakeLists.txt b/src/osgEarth/CMakeLists.txt
index 25e0e0c..2175736 100644
--- a/src/osgEarth/CMakeLists.txt
+++ b/src/osgEarth/CMakeLists.txt
@@ -7,6 +7,8 @@ ENDIF(DYNAMIC_OSGEARTH)
 OPTION(CURL_IS_STATIC "on if curl is a static lib " ON)
 MARK_AS_ADVANCED(CURL_IS_STATIC)
 
+ADD_DEFINITIONS(-DTIXML_USE_STL)
+
 IF(WIN32)
     SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:MSVCRT")
     IF(CURL_IS_STATIC)
@@ -61,6 +63,10 @@ SET(LIB_PUBLIC_HEADERS
     Locators
     LocalTangentPlane
     Map
+    MapCallback
+    MapFrame
+    MapInfo
+    MapModelChange
     MapNode
     MapNodeObserver
     MapNodeOptions
@@ -81,7 +87,7 @@ SET(LIB_PUBLIC_HEADERS
     Random
     Registry
     Revisioning
-    ShaderComposition
+    ShaderFactory
     ShaderGenerator
     ShaderUtils
     SparseTexture2DArray
@@ -105,6 +111,7 @@ SET(LIB_PUBLIC_HEADERS
     Version
     VerticalDatum
     Viewpoint
+    VirtualProgram
     XmlUtils
 )
 
@@ -165,6 +172,9 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     Locators.cpp
     LocalTangentPlane.cpp
     Map.cpp
+    MapCallback.cpp
+    MapFrame.cpp
+    MapInfo.cpp
     MapNode.cpp
     MapNodeOptions.cpp
     MapOptions.cpp
@@ -184,7 +194,7 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     Random.cpp
     Registry.cpp
     Revisioning.cpp
-    ShaderComposition.cpp
+    ShaderFactory.cpp
     ShaderGenerator.cpp
     ShaderUtils.cpp
     SparseTexture2DArray.cpp
@@ -208,6 +218,7 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     Version.cpp
     VerticalDatum.cpp
     Viewpoint.cpp
+    VirtualProgram.cpp
     XmlUtils.cpp
 )
 
diff --git a/src/osgEarth/Cache.cpp b/src/osgEarth/Cache.cpp
index 06c26b2..d96a8bf 100644
--- a/src/osgEarth/Cache.cpp
+++ b/src/osgEarth/Cache.cpp
@@ -69,7 +69,7 @@ Cache*
 CacheFactory::create( const CacheOptions& options )
 {
     osg::ref_ptr<Cache> result =0L;
-    OE_INFO << LC << "Initializing cache of type \"" << options.getDriver() << "\"" << std::endl;
+    OE_DEBUG << LC << "Initializing cache of type \"" << options.getDriver() << "\"" << std::endl;
 
     if ( options.getDriver().empty() )
     {
diff --git a/src/osgEarth/CacheSeed.cpp b/src/osgEarth/CacheSeed.cpp
index b3d2759..4cfeb9b 100644
--- a/src/osgEarth/CacheSeed.cpp
+++ b/src/osgEarth/CacheSeed.cpp
@@ -18,6 +18,7 @@
 */
 
 #include <osgEarth/CacheSeed>
+#include <osgEarth/MapFrame>
 #include <OpenThreads/ScopedLock>
 #include <limits.h>
 
@@ -27,12 +28,12 @@ using namespace osgEarth;
 using namespace OpenThreads;
 
 CacheSeed::CacheSeed():
-          _minLevel(0),
-          _maxLevel(12),          
-          _total(0),
-          _completed(0)
-          {
-          }
+_minLevel (0),
+_maxLevel (12),
+_total    (0),
+_completed(0)
+{
+}
 
 void CacheSeed::seed( Map* map )
 {
@@ -60,7 +61,7 @@ void CacheSeed::seed( Map* map )
     //Assumes the the TileSource will perform the caching for us when we call createImage
     for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ )
     {
-		ImageLayer* layer = i->get();
+        ImageLayer* layer = i->get();
         TileSource* src   = layer->getTileSource();
 
         const ImageLayerOptions& opt = layer->getImageLayerOptions();
@@ -85,16 +86,16 @@ void CacheSeed::seed( Map* map )
         {
             hasCaches = true;
 
-			if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
+            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                 src_min_level = opt.minLevel().get();
-			if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
+            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                 src_max_level = opt.maxLevel().get();
         }
     }
 
     for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ )
     {
-		ElevationLayer* layer = i->get();
+        ElevationLayer* layer = i->get();
         TileSource*     src   = layer->getTileSource();
         const ElevationLayerOptions& opt = layer->getElevationLayerOptions();
 
@@ -118,11 +119,11 @@ void CacheSeed::seed( Map* map )
         {
             hasCaches = true;
 
-			if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
+            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                 src_min_level = opt.minLevel().get();
-			if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
+            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                 src_max_level = opt.maxLevel().get();
-		}
+        }
     }
 
     if ( !hasCaches )
@@ -267,7 +268,7 @@ void CacheSeed::seed( Map* map )
 
     _total = _completed;
 
-    if ( _progress.valid()) _progress->reportProgress(_completed, _total, "Finished");
+    if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished");
 }
 
 void CacheSeed::incrementCompleted( unsigned int total ) const
@@ -293,8 +294,8 @@ CacheSeed::processKey(const MapFrame& mapf, const TileKey& key ) const
         incrementCompleted( 1 );
         }
 
-    	if ( _progress.valid() && _progress->isCanceled() )
-	        return; // Task has been cancelled by user
+        if ( _progress.valid() && _progress->isCanceled() )
+            return; // Task has been cancelled by user
 
         if ( _progress.valid() && gotData && _progress->reportProgress(_completed, _total, std::string("Cached tile: ") + key.str()) )
             return; // Canceled
diff --git a/src/osgEarth/Capabilities.cpp b/src/osgEarth/Capabilities.cpp
index fe42b5d..8e331fb 100644
--- a/src/osgEarth/Capabilities.cpp
+++ b/src/osgEarth/Capabilities.cpp
@@ -189,7 +189,11 @@ _maxUniformBlockSize    ( 0 )
 #endif
         OE_INFO << LC << "  Max lights = " << _maxLights << std::endl;
 
-        _supportsGLSL = GL2->isGlslSupported();
+        
+        if ( ::getenv("OSGEARTH_NO_GLSL") )
+            _supportsGLSL = false;
+        else
+            _supportsGLSL = GL2->isGlslSupported();
         OE_INFO << LC << "  GLSL = " << SAYBOOL(_supportsGLSL) << std::endl;
 
         if ( _supportsGLSL )
@@ -219,7 +223,8 @@ _maxUniformBlockSize    ( 0 )
         _supportsTwoSidedStencil = osg::isGLExtensionSupported( id, "GL_EXT_stencil_two_side" );
         OE_INFO << LC << "  2-sided stencils = " << SAYBOOL(_supportsTwoSidedStencil) << std::endl;
 
-        _supportsDepthPackedStencilBuffer = osg::isGLExtensionSupported( id, "GL_EXT_packed_depth_stencil" );
+        _supportsDepthPackedStencilBuffer = osg::isGLExtensionSupported( id, "GL_EXT_packed_depth_stencil" ) || 
+                                            osg::isGLExtensionSupported( id, "GL_OES_packed_depth_stencil" );
         OE_INFO << LC << "  depth-packed stencil = " << SAYBOOL(_supportsDepthPackedStencilBuffer) << std::endl;
 
         _supportsOcclusionQuery = osg::isGLExtensionSupported( id, "GL_ARB_occlusion_query" );
@@ -228,11 +233,17 @@ _maxUniformBlockSize    ( 0 )
         _supportsDrawInstanced = osg::isGLExtensionOrVersionSupported( id, "GL_EXT_draw_instanced", 3.1f );
         OE_INFO << LC << "  draw instanced = " << SAYBOOL(_supportsDrawInstanced) << std::endl;
 
+        glGetIntegerv( GL_MAX_UNIFORM_BLOCK_SIZE, &_maxUniformBlockSize );
+        OE_INFO << LC << "  max uniform block size = " << _maxUniformBlockSize << std::endl;
+
         _supportsUniformBufferObjects = osg::isGLExtensionOrVersionSupported( id, "GL_ARB_uniform_buffer_object", 2.0f );
         OE_INFO << LC << "  uniform buffer objects = " << SAYBOOL(_supportsUniformBufferObjects) << std::endl;
 
-        glGetIntegerv( GL_MAX_UNIFORM_BLOCK_SIZE, &_maxUniformBlockSize );
-        OE_INFO << LC << "  max uniform block size = " << _maxUniformBlockSize << std::endl;
+        if ( _supportsUniformBufferObjects && _maxUniformBlockSize == 0 )
+        {
+            OE_INFO << LC << "  ...but disabled, since UBO block size reports zero" << std::endl;
+            _supportsUniformBufferObjects = false;
+        }
 
 
         //_supportsTexture2DLod = osg::isGLExtensionSupported( id, "GL_ARB_shader_texture_lod" );
diff --git a/src/osgEarth/CompositeTileSource.cpp b/src/osgEarth/CompositeTileSource.cpp
index 6918b1d..8c7ab12 100644
--- a/src/osgEarth/CompositeTileSource.cpp
+++ b/src/osgEarth/CompositeTileSource.cpp
@@ -302,7 +302,7 @@ CompositeTileSource::createImage(const TileKey&    key,
                     {
                         //We got an image, but now we need to crop it to match the incoming key's extents
                         GeoImage geoImage( image.get(), parentKey.getExtent());
-                        GeoImage cropped = geoImage.crop( key.getExtent(), true, textureSize.x(), textureSize.y());
+                        GeoImage cropped = geoImage.crop( key.getExtent(), true, textureSize.x(), textureSize.y(), *source->_options.bilinearReprojection());
                         image = cropped.getImage();
                     }
 
diff --git a/src/osgEarth/Containers b/src/osgEarth/Containers
index d53ec2f..b40b40b 100644
--- a/src/osgEarth/Containers
+++ b/src/osgEarth/Containers
@@ -22,6 +22,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/ThreadingUtils>
 #include <list>
+#include <vector>
 
 namespace osgEarth
 {
diff --git a/src/osgEarth/CullingUtils.cpp b/src/osgEarth/CullingUtils.cpp
index f8341c9..a30ec69 100644
--- a/src/osgEarth/CullingUtils.cpp
+++ b/src/osgEarth/CullingUtils.cpp
@@ -591,4 +591,4 @@ void OcclusionCullingCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
     {
         traverse( node, nv );
     }
-}
\ No newline at end of file
+}
diff --git a/src/osgEarth/DrapeableNode.cpp b/src/osgEarth/DrapeableNode.cpp
index 6dee4fd..1d59d5f 100644
--- a/src/osgEarth/DrapeableNode.cpp
+++ b/src/osgEarth/DrapeableNode.cpp
@@ -30,20 +30,12 @@ using namespace osgEarth;
 
 namespace
 {
-#if 0
-    // Custom group that limits traversals to CULL and any visitor internal to
-    // the operation of the OverlayDecorator.
-    struct OverlayTraversalGroup : public osg::Group {
-        virtual void traverse(osg::NodeVisitor& nv) {
-            if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR ||  
-                 dynamic_cast<OverlayDecorator::InternalNodeVisitor*>(&nv) )
-            {
-                osg::Group::traverse(nv);
-            }
-        }
-    };
-#endif
-
+    /**
+     * When draping is enabled, the actual draped graph goes under an OverlayProxy
+     * group. It tracks the accumulated stateset and nodemask of the Drapeable
+     * itself and applies it to the draped geometry (which is installed under the
+     * MapNode's OverlayDecorator).
+     */
     struct OverlayProxy : public osg::Group
     {
         OverlayProxy( osg::Node* owner ) 
@@ -75,7 +67,7 @@ namespace
 
                     // first check the owner's traversal mask.
                     bool visible = true;
-                    for( int k = 0; visible && k < ownerPath.size(); ++k )
+                    for( int k = 0; visible && k < (int)ownerPath.size(); ++k )
                     {
                         visible = nv.validNodeMask(*ownerPath[k]);
                     }
@@ -85,12 +77,12 @@ namespace
                         // find the intersection point:
                         int i = findIndexOfNodePathConvergence( visitorPath, ownerPath );
 
-                        if ( i >= 0 && i < ownerPath.size()-1 )
+                        if ( i >= 0 && i < (int)ownerPath.size()-1 )
                         {
                             osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
 
                             int pushes = 0;
-                            for( int k = i+1; k < ownerPath.size(); ++k )
+                            for( int k = i+1; k < (int)ownerPath.size(); ++k )
                             {
                                 osg::Node* node = ownerPath[k];
                                 osg::StateSet* ss = ownerPath[k]->getStateSet();
diff --git a/src/osgEarth/DrawInstanced b/src/osgEarth/DrawInstanced
index e74d3f4..08b60da 100644
--- a/src/osgEarth/DrawInstanced
+++ b/src/osgEarth/DrawInstanced
@@ -21,7 +21,7 @@
 #define OSGEARTH_DRAW_INSTANCED_H 1
 
 #include <osgEarth/Common>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osg/NodeVisitor>
 #include <osg/Geode>
 
diff --git a/src/osgEarth/DrawInstanced.cpp b/src/osgEarth/DrawInstanced.cpp
index b51c301..2cf5b9f 100644
--- a/src/osgEarth/DrawInstanced.cpp
+++ b/src/osgEarth/DrawInstanced.cpp
@@ -113,29 +113,28 @@ DrawInstanced::createDrawInstancedProgram()
 
     if ( Registry::capabilities().supportsUniformBufferObjects() )
     {
+        // note: no newlines in the layout() line, please
         buf << "#extension GL_ARB_uniform_buffer_object : enable\n"
-            << "layout(std140) uniform osgearth_InstanceModelData\n"
-            << "{\n"
-            <<     "mat4 osgearth_instanceModelMatrix[ " << MAX_COUNT_UBO << "];\n"
-            << "};\n";
+            << "layout(std140) uniform oe_di_modelData { "
+            <<     "mat4 oe_di_modelMatrix[" << MAX_COUNT_UBO << "]; } ;\n";
 
-        vp->getTemplate()->addBindUniformBlock( "osgearth_InstanceModelData", 0 );
+        vp->getTemplate()->addBindUniformBlock( "oe_di_modelData", 0 );
     }
     else
     {
-        buf << "uniform mat4 osgearth_instanceModelMatrix[" << MAX_COUNT_ARRAY << "];\n";
+        buf << "uniform mat4 oe_di_modelMatrix[" << MAX_COUNT_ARRAY << "];\n";
     }
 
-    buf << "void osgearth_setInstancePosition()\n"
+    buf << "void oe_di_setPosition()\n"
         << "{\n"
-        << "    gl_Position = gl_ModelViewProjectionMatrix * osgearth_instanceModelMatrix[gl_InstanceID] * gl_Vertex; \n"
+        << "    gl_Position = gl_ModelViewProjectionMatrix * oe_di_modelMatrix[gl_InstanceID] * gl_Vertex; \n"
         << "}\n";
 
     std::string src;
     src = buf.str();
 
     vp->setFunction(
-        "osgearth_setInstancePosition",
+        "oe_di_setPosition",
         src,
         ShaderComp::LOCATION_VERTEX_PRE_COLORING );
 
@@ -260,7 +259,7 @@ DrawInstanced::convertGraphToUseDrawInstanced( osg::Group* parent )
             {
                 // assign the matrices to the uniform array:
                 ArrayUniform uniform(
-                    "osgearth_instanceModelMatrix", 
+                    "oe_di_modelMatrix", 
                     osg::Uniform::FLOAT_MAT4,
                     sliceGroup->getOrCreateStateSet(),
                     currentSize );
diff --git a/src/osgEarth/ElevationLayer b/src/osgEarth/ElevationLayer
index f2cb239..8cf71fc 100644
--- a/src/osgEarth/ElevationLayer
+++ b/src/osgEarth/ElevationLayer
@@ -21,6 +21,7 @@
 #define OSGEARTH_ELEVATION_TERRAIN_LAYER_H 1
 
 #include <osgEarth/TerrainLayer>
+#include <osg/MixinVector>
 
 namespace osgEarth
 {
@@ -45,7 +46,7 @@ namespace osgEarth
         virtual void mergeConfig( const Config& conf );
         
     private:
-        void fromConfig( const Config& conf );            
+        void fromConfig( const Config& conf );
         void setDefaults();
     };
 
@@ -143,7 +144,26 @@ namespace osgEarth
         void init();
     };
 
-    typedef std::vector< osg::ref_ptr<ElevationLayer> > ElevationLayerVector;
+
+    /**
+     * Vector of elevation layers, with added methods.
+     */
+    class OSGEARTH_EXPORT ElevationLayerVector : public osg::MixinVector< osg::ref_ptr<ElevationLayer> >
+    {
+    public:
+        /**
+         * Creates a heightfield object by sampling this elevation layer vector.
+         */
+        bool createHeightField(
+            const TileKey&                  key,
+            bool                            fallback,
+            const Profile*                  haeProfile,
+            ElevationInterpolation          interpolation,
+            ElevationSamplePolicy           samplePolicy,
+            osg::ref_ptr<osg::HeightField>& out_result,
+            bool*                           out_isFallback,
+            ProgressCallback*               progress ) const;
+    };
 
 } // namespace osgEarth
 
diff --git a/src/osgEarth/ElevationLayer.cpp b/src/osgEarth/ElevationLayer.cpp
index a9dc133..e51dbfb 100644
--- a/src/osgEarth/ElevationLayer.cpp
+++ b/src/osgEarth/ElevationLayer.cpp
@@ -439,3 +439,248 @@ ElevationLayer::createHeightField(const TileKey&    key,
         GeoHeightField( result, key.getExtent() ) :
         GeoHeightField::INVALID;
 }
+
+
+//------------------------------------------------------------------------
+
+
+bool
+ElevationLayerVector::createHeightField(const TileKey&                  key,
+                                        bool                            fallback,
+                                        const Profile*                  haeProfile,
+                                        ElevationInterpolation          interpolation,
+                                        ElevationSamplePolicy           samplePolicy,
+                                        osg::ref_ptr<osg::HeightField>& out_result,
+                                        bool*                           out_isFallback,
+                                        ProgressCallback*               progress )  const
+{        
+    unsigned lowestLOD = key.getLevelOfDetail();
+    bool hfInitialized = false;
+
+    //Get a HeightField for each of the enabled layers
+    GeoHeightFieldVector heightFields;
+
+    //The number of fallback heightfields we have
+    int numFallbacks = 0;
+
+    //Default to being fallback data.
+    if ( out_isFallback )
+    {
+        *out_isFallback = true;
+    }
+
+    // if the caller provided an "HAE map profile", he wants an HAE elevation grid even if
+    // the map profile has a vertical datum. This is the usual case when building the 3D
+    // terrain, for example. Construct a temporary key that doesn't have the vertical
+    // datum info and use that to query the elevation data.
+    TileKey keyToUse = key;
+    if ( haeProfile )
+    {
+        keyToUse = TileKey(key.getLevelOfDetail(), key.getTileX(), key.getTileY(), haeProfile );
+    }
+
+    // Generate a heightfield for each elevation layer.
+
+    unsigned defElevSize = 8;
+
+    for( ElevationLayerVector::const_iterator i = this->begin(); i != this->end(); i++ )
+    {
+        ElevationLayer* layer = i->get();
+        if ( layer->getVisible() )
+        {
+            GeoHeightField geoHF = layer->createHeightField( keyToUse, progress );
+
+            // if "fallback" is set, try to fall back on lower LODs.
+            if ( !geoHF.valid() && fallback )
+            {
+                TileKey hf_key = keyToUse.createParentKey();
+
+                while ( hf_key.valid() && !geoHF.valid() )
+                {
+                    geoHF = layer->createHeightField( hf_key, progress );
+                    if ( !geoHF.valid() )
+                        hf_key = hf_key.createParentKey();
+                }
+
+                if ( geoHF.valid() )
+                {
+                    if ( hf_key.getLevelOfDetail() < lowestLOD )
+                        lowestLOD = hf_key.getLevelOfDetail();
+
+                    //This HeightField is fallback data, so increment the count.
+                    numFallbacks++;
+                }
+            }
+
+            if ( geoHF.valid() )
+            {
+                heightFields.push_back( geoHF );
+            }
+        }
+    }
+
+    //If any of the layers produced valid data then it's not considered a fallback
+    if ( out_isFallback )
+    {
+        *out_isFallback = (numFallbacks == heightFields.size());
+        //OE_NOTICE << "Num fallbacks=" << numFallbacks << " numHeightFields=" << heightFields.size() << " is fallback " << *out_isFallback << std::endl;
+    }   
+
+    if ( heightFields.size() == 0 )
+    {            
+        //If we got no heightfields but were requested to fallback, create an empty heightfield.
+        if ( fallback )
+        {
+            out_result = HeightFieldUtils::createReferenceHeightField( keyToUse.getExtent(), defElevSize, defElevSize );                
+            return true;
+        }
+        else
+        {
+            //We weren't requested to fallback so just return.
+            return false;
+        }
+    }
+
+    else if (heightFields.size() == 1)
+    {
+        if ( lowestLOD == key.getLevelOfDetail() )
+        {
+            //If we only have on heightfield, just return it.
+            out_result = heightFields[0].takeHeightField();
+        }
+        else
+        {
+            GeoHeightField geoHF = heightFields[0].createSubSample( key.getExtent(), interpolation);
+            out_result = geoHF.takeHeightField();
+            hfInitialized = true;
+        }
+    }
+
+    else
+    {
+        //If we have multiple heightfields, we need to composite them together.
+        unsigned int width = 0;
+        unsigned int height = 0;
+
+        for (GeoHeightFieldVector::const_iterator i = heightFields.begin(); i < heightFields.end(); ++i)
+        {
+            if (i->getHeightField()->getNumColumns() > width) 
+                width = i->getHeightField()->getNumColumns();
+            if (i->getHeightField()->getNumRows() > height) 
+                height = i->getHeightField()->getNumRows();
+        }
+        out_result = new osg::HeightField();
+        out_result->allocate( width, height );
+
+        //Go ahead and set up the heightfield so we don't have to worry about it later
+        double minx, miny, maxx, maxy;
+        key.getExtent().getBounds(minx, miny, maxx, maxy);
+        double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
+        double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);
+
+        const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();
+
+        //Create the new heightfield by sampling all of them.
+        for (unsigned int c = 0; c < width; ++c)
+        {
+            double x = minx + (dx * (double)c);
+            for (unsigned r = 0; r < height; ++r)
+            {
+                double y = miny + (dy * (double)r);
+
+                //Collect elevations from all of the layers. Iterate BACKWARDS because the last layer
+                // is the highest priority.
+                std::vector<float> elevations;
+                for( GeoHeightFieldVector::reverse_iterator itr = heightFields.rbegin(); itr != heightFields.rend(); ++itr )
+                {
+                    const GeoHeightField& geoHF = *itr;
+
+                    float elevation = 0.0f;
+                    if ( geoHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) )
+                    {
+                        if (elevation != NO_DATA_VALUE)
+                        {
+                            elevations.push_back(elevation);
+                        }
+                    }
+                }
+
+                float elevation = NO_DATA_VALUE;
+
+                //The list of elevations only contains valid values
+                if (elevations.size() > 0)
+                {
+                    if (samplePolicy == SAMPLE_FIRST_VALID)
+                    {
+                        elevation = elevations[0];
+                    }
+                    else if (samplePolicy == SAMPLE_HIGHEST)
+                    {
+                        elevation = -FLT_MAX;
+                        for (unsigned int i = 0; i < elevations.size(); ++i)
+                        {
+                            if (elevation < elevations[i]) elevation = elevations[i];
+                        }
+                    }
+                    else if (samplePolicy == SAMPLE_LOWEST)
+                    {
+                        elevation = FLT_MAX;
+                        for (unsigned i = 0; i < elevations.size(); ++i)
+                        {
+                            if (elevation > elevations[i]) elevation = elevations[i];
+                        }
+                    }
+                    else if (samplePolicy == SAMPLE_AVERAGE)
+                    {
+                        elevation = 0.0;
+                        for (unsigned i = 0; i < elevations.size(); ++i)
+                        {
+                            elevation += elevations[i];
+                        }
+                        elevation /= (float)elevations.size();
+                    }
+                }
+                out_result->setHeight(c, r, elevation);
+            }
+        }
+    }
+
+    // Replace any NoData areas with the reference value. This is zero for HAE datums,
+    // and some geoid height for orthometric datums.
+    if (out_result.valid())
+    {
+        const Geoid*         geoid = 0L;
+        const VerticalDatum* vdatum = key.getProfile()->getSRS()->getVerticalDatum();
+
+        if ( haeProfile && vdatum )
+        {
+            geoid = vdatum->getGeoid();
+        }
+
+        HeightFieldUtils::resolveInvalidHeights(
+            out_result.get(),
+            key.getExtent(),
+            NO_DATA_VALUE,
+            geoid );
+
+        //ReplaceInvalidDataOperator o;
+        //o.setValidDataOperator(new osgTerrain::NoDataValue(NO_DATA_VALUE));
+        //o( out_result.get() );
+    }
+
+    //Initialize the HF values for osgTerrain
+    if (out_result.valid() && !hfInitialized )
+    {   
+        //Go ahead and set up the heightfield so we don't have to worry about it later
+        double minx, miny, maxx, maxy;
+        key.getExtent().getBounds(minx, miny, maxx, maxy);
+        out_result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) );
+        double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
+        double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);
+        out_result->setXInterval( dx );
+        out_result->setYInterval( dy );
+        out_result->setBorderWidth( 0 );
+    }
+
+    return out_result.valid();
+}
diff --git a/src/osgEarth/ElevationQuery b/src/osgEarth/ElevationQuery
index 06bc752..c4082d4 100644
--- a/src/osgEarth/ElevationQuery
+++ b/src/osgEarth/ElevationQuery
@@ -19,7 +19,7 @@
 #ifndef OSGEARTH_ELEVATION_QUERY_H
 #define OSGEARTH_ELEVATION_QUERY_H 1
 
-#include <osgEarth/Map>
+#include <osgEarth/MapFrame>
 #include <osgEarth/Containers>
 
 namespace osgEarth
diff --git a/src/osgEarth/FadeEffect.cpp b/src/osgEarth/FadeEffect.cpp
index 923e0ff..fae0b9e 100644
--- a/src/osgEarth/FadeEffect.cpp
+++ b/src/osgEarth/FadeEffect.cpp
@@ -17,7 +17,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarth/FadeEffect>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
 #include <osgUtil/CullVisitor>
 
 using namespace osgEarth;
@@ -28,24 +30,30 @@ namespace
 {
     char* FadeEffectVertexShader =
         "#version " GLSL_VERSION_STR "\n"
-        "uniform float osgearth_FadeEffect_duration; \n"
-        "uniform float osgearth_FadeEffect_startTime; \n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float; \n"
+#endif
+        "uniform float oe_FadeEffect_duration; \n"
+        "uniform float oe_FadeEffect_startTime; \n"
         "uniform float osg_FrameTime; \n"
-        "varying float osgearth_FadeEffect_opacity; \n"
+        "varying float oe_FadeEffect_opacity; \n"
 
-        "void vertFadeEffect() \n"
+        "void oe_vertFadeEffect() \n"
         "{ \n"
-        "    float t = (osg_FrameTime-osgearth_FadeEffect_startTime)/osgearth_FadeEffect_duration; \n"
-        "    osgearth_FadeEffect_opacity = clamp( t, 0.0, 1.0 ); \n"
+        "    float t = (osg_FrameTime-oe_FadeEffect_startTime)/oe_FadeEffect_duration; \n"
+        "    oe_FadeEffect_opacity = clamp( t, 0.0, 1.0 ); \n"
         "} \n";
 
     char* FadeEffectFragmentShader = 
         "#version " GLSL_VERSION_STR "\n"
-        "varying float osgearth_FadeEffect_opacity; \n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float; \n"
+#endif
+        "varying float oe_FadeEffect_opacity; \n"
 
-        "void fragFadeEffect( inout vec4 color ) \n"
+        "void oe_fragFadeEffect( inout vec4 color ) \n"
         "{ \n"
-        "    color.a *= osgearth_FadeEffect_opacity; \n"
+        "    color.a *= oe_FadeEffect_opacity; \n"
         "} \n";
 }
 
@@ -54,21 +62,24 @@ namespace
 osg::Uniform*
 FadeEffect::createStartTimeUniform()
 {
-    return new osg::Uniform( osg::Uniform::FLOAT, "osgearth_FadeEffect_startTime" );
+    return new osg::Uniform( osg::Uniform::FLOAT, "oe_FadeEffect_startTime" );
 }
 
 FadeEffect::FadeEffect()
 {
     osg::StateSet* ss = this->getOrCreateStateSet();
 
-    VirtualProgram* vp = new VirtualProgram();
+    if ( Registry::capabilities().supportsGLSL() )
+    {
+        VirtualProgram* vp = new VirtualProgram();
 
-    vp->setFunction( "vertFadeEffect", FadeEffectVertexShader,   ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
-    vp->setFunction( "fragFadeEffect", FadeEffectFragmentShader, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+        vp->setFunction( "oe_vertFadeEffect", FadeEffectVertexShader,   ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
+        vp->setFunction( "oe_fragFadeEffect", FadeEffectFragmentShader, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
 
-    ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
+        ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
+    }
 
-    _fadeDuration = new osg::Uniform( osg::Uniform::FLOAT, "osgearth_FadeEffect_duration" );
+    _fadeDuration = new osg::Uniform( osg::Uniform::FLOAT, "oe_FadeEffect_duration" );
     _fadeDuration->set( 1.0f );
     ss->addUniform( _fadeDuration );
 
@@ -95,10 +106,13 @@ namespace
 {
     char* FadeLODFragmentShader = 
         "#version " GLSL_VERSION_STR "\n"
-        "varying float osgearth_FadeLOD_opacity; \n"
-        "void fragFadeLOD( inout vec4 color ) \n"
+#ifdef OSG_GLES_AVAILABLE
+        "precision mediump float; \n"
+#endif
+        "varying float oe_FadeLOD_opacity; \n"
+        "void oe_fragFadeLOD( inout vec4 color ) \n"
         "{ \n"
-        "    color.a *= osgearth_FadeLOD_opacity; \n"
+        "    color.a *= oe_FadeLOD_opacity; \n"
         "} \n";
 }
 
@@ -113,16 +127,19 @@ _maxPixelExtent( FLT_MAX ),
 _minFadeExtent ( 0.0f ),
 _maxFadeExtent ( 0.0f )
 {
-    VirtualProgram* vp = new VirtualProgram();
+    if ( Registry::capabilities().supportsGLSL() )
+    {
+        VirtualProgram* vp = new VirtualProgram();
 
-    vp->setFunction(
-        "fragFadeLOD",
-        FadeLODFragmentShader,
-        ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+        vp->setFunction(
+            "oe_fragFadeLOD",
+            FadeLODFragmentShader,
+            ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
 
-    osg::StateSet* ss = getOrCreateStateSet();
+        osg::StateSet* ss = getOrCreateStateSet();
 
-    ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
+        ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
+    }
 }
 
 
@@ -135,7 +152,7 @@ FadeLOD::traverse( osg::NodeVisitor& nv )
         PerViewData& data = _perViewData.get(cv);
         if ( !data._opacity.valid() )
         {
-            data._opacity = new osg::Uniform(osg::Uniform::FLOAT, "osgearth_FadeLOD_opacity");
+            data._opacity = new osg::Uniform(osg::Uniform::FLOAT, "oe_FadeLOD_opacity");
             data._stateSet = new osg::StateSet();
             data._stateSet->addUniform( data._opacity.get() );
         }
diff --git a/src/osgEarth/GeoData b/src/osgEarth/GeoData
index b1386d2..9d62116 100644
--- a/src/osgEarth/GeoData
+++ b/src/osgEarth/GeoData
@@ -558,7 +558,8 @@ namespace osgEarth
             const GeoExtent& extent,
             bool exact = false,
             unsigned int width = 0,
-            unsigned int height = 0) const;
+            unsigned int height = 0,
+            bool useBilinearInterpolation = true) const;
 
         /**
          * Warps the image into a new spatial reference system.
@@ -576,7 +577,8 @@ namespace osgEarth
             const SpatialReference* to_srs,
             const GeoExtent* to_extent = 0,
             unsigned int width = 0,
-            unsigned int height = 0) const;
+            unsigned int height = 0,
+            bool useBilinearInterpolation = true) const;
 
         /**
          * Adds a one-pixel transparent border around an image.
diff --git a/src/osgEarth/GeoData.cpp b/src/osgEarth/GeoData.cpp
index 13b4714..26ef307 100644
--- a/src/osgEarth/GeoData.cpp
+++ b/src/osgEarth/GeoData.cpp
@@ -1193,7 +1193,7 @@ GeoImage::getUnitsPerPixel() const {
 }
 
 GeoImage
-GeoImage::crop( const GeoExtent& extent, bool exact, unsigned int width, unsigned int height  ) const
+GeoImage::crop( const GeoExtent& extent, bool exact, unsigned int width, unsigned int height, bool useBilinearInterpolation) const
 {
     //Check for equivalence
     if ( extent.getSRS()->isEquivalentTo( getSRS() ) )
@@ -1218,7 +1218,7 @@ GeoImage::crop( const GeoExtent& extent, bool exact, unsigned int width, unsigne
             }
 
             //Note:  Passing in the current SRS simply forces GDAL to not do any warping
-            return reproject( getSRS(), &extent, width, height);
+            return reproject( getSRS(), &extent, width, height, useBilinearInterpolation);
         }
         else
         {
@@ -1377,7 +1377,7 @@ createDataSetFromImage(const osg::Image* image, double minX, double minY, double
 static osg::Image*
 reprojectImage(osg::Image* srcImage, const std::string srcWKT, double srcMinX, double srcMinY, double srcMaxX, double srcMaxY,
                const std::string destWKT, double destMinX, double destMinY, double destMaxX, double destMaxY,
-               int width = 0, int height = 0)
+               int width = 0, int height = 0, bool useBilinearInterpolation = true)
 {
     GDAL_SCOPED_LOCK;
 	osg::Timer_t start = osg::Timer::instance()->tick();
@@ -1406,11 +1406,20 @@ reprojectImage(osg::Image* srcImage, const std::string srcWKT, double srcMinX, d
    
     GDALDataset* destDS = createMemDS(width, height, destMinX, destMinY, destMaxX, destMaxY, destWKT);
 
-    GDALReprojectImage(srcDS, NULL,
-                       destDS, NULL,
-                       //GDALResampleAlg::GRA_NearestNeighbour,
-                       GRA_Bilinear,
-                       0,0,0,0,0);                    
+    if (useBilinearInterpolation == true)
+    {
+        GDALReprojectImage(srcDS, NULL,
+                           destDS, NULL,
+                           GRA_Bilinear,
+                           0,0,0,0,0);
+    }
+    else
+    {
+        GDALReprojectImage(srcDS, NULL,
+                           destDS, NULL,
+                           GRA_NearestNeighbour,
+                           0,0,0,0,0);
+    }
 
     osg::Image* result = createImageFromDataset(destDS);
     
@@ -1617,7 +1626,7 @@ namespace
 
 
 GeoImage
-GeoImage::reproject(const SpatialReference* to_srs, const GeoExtent* to_extent, unsigned int width, unsigned int height) const
+GeoImage::reproject(const SpatialReference* to_srs, const GeoExtent* to_extent, unsigned int width, unsigned int height, bool useBilinearInterpolation) const
 {  
     GeoExtent destExtent;
     if (to_extent)
@@ -1650,7 +1659,7 @@ GeoImage::reproject(const SpatialReference* to_srs, const GeoExtent* to_extent,
             getExtent().xMin(), getExtent().yMin(), getExtent().xMax(), getExtent().yMax(),
             to_srs->getWKT(),
             destExtent.xMin(), destExtent.yMin(), destExtent.xMax(), destExtent.yMax(),
-            width, height);
+            width, height, useBilinearInterpolation);
     }   
     return GeoImage(resultImage, destExtent);
 }
diff --git a/src/osgEarth/HTTPClient b/src/osgEarth/HTTPClient
index bac1527..85f7ece 100644
--- a/src/osgEarth/HTTPClient
+++ b/src/osgEarth/HTTPClient
@@ -210,6 +210,15 @@ namespace osgEarth
             TODO: This should probably move into the Registry */
         static void setProxySettings( const ProxySettings &proxySettings );
 
+        /**
+           Gets the timeout in seconds to use for HTTP requests.*/
+        static long getTimeout();
+
+        /**
+           Sets the timeout in seconds to use for HTTP requests.
+           Setting to 0 (default) is infinite timeout */
+        void setTimeout( long timeout );
+
 
     public:
         /**
diff --git a/src/osgEarth/HTTPClient.cpp b/src/osgEarth/HTTPClient.cpp
index 8e5f149..b3e3316 100644
--- a/src/osgEarth/HTTPClient.cpp
+++ b/src/osgEarth/HTTPClient.cpp
@@ -293,13 +293,19 @@ HTTPResponse::getHeadersAsConfig() const
 #define USER_AGENT "osgearth" QUOTE(OSGEARTH_MAJOR_VERSION) "." QUOTE(OSGEARTH_MINOR_VERSION)
 
 
-static optional<ProxySettings>     _proxySettings;
-static std::string                 _userAgent = USER_AGENT;
-
 namespace
 {
     // per-thread client map (must be global scope)
     static Threading::PerThread<HTTPClient> s_clientPerThread;
+
+    static optional<ProxySettings>     s_proxySettings;
+
+    static std::string                 s_userAgent = USER_AGENT;
+
+    static long                        s_timeout = 0;
+
+    // HTTP debugging.
+    static bool                        s_HTTP_DEBUG = false;
 }
 
 HTTPClient&
@@ -333,7 +339,7 @@ HTTPClient::initializeImpl()
     _curl_handle = curl_easy_init();
 
     //Get the user agent
-    std::string userAgent = _userAgent;
+    std::string userAgent = s_userAgent;
     const char* userAgentEnv = getenv("OSGEARTH_USERAGENT");
     if (userAgentEnv)
     {
@@ -348,6 +354,13 @@ HTTPClient::initializeImpl()
         OE_WARN << LC << "Simulating a network error with Response Code = " << _simResponseCode << std::endl;
     }
 
+    // Dumps out HTTP request/response info
+    if ( ::getenv("OSGEARTH_HTTP_DEBUG") )
+    {
+        s_HTTP_DEBUG = true;
+        OE_WARN << LC << "HTTP debugging enabled" << std::endl;
+    }
+
     OE_DEBUG << LC << "HTTPClient setting userAgent=" << userAgent << std::endl;
 
     curl_easy_setopt( _curl_handle, CURLOPT_USERAGENT, userAgent.c_str() );
@@ -355,8 +368,15 @@ HTTPClient::initializeImpl()
     curl_easy_setopt( _curl_handle, CURLOPT_FOLLOWLOCATION, (void*)1 );
     curl_easy_setopt( _curl_handle, CURLOPT_MAXREDIRS, (void*)5 );
     curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSFUNCTION, &CurlProgressCallback);
-    curl_easy_setopt( _curl_handle, CURLOPT_NOPROGRESS, (void*)0 ); //FALSE);
-    //curl_easy_setopt( _curl_handle, CURLOPT_TIMEOUT, 1L );
+    curl_easy_setopt( _curl_handle, CURLOPT_NOPROGRESS, (void*)0 ); //FALSE);    
+    long timeout = s_timeout;
+    const char* timeoutEnv = getenv("OSGEARTH_HTTP_TIMEOUT");
+    if (timeoutEnv)
+    {        
+        timeout = osgEarth::as<long>(std::string(timeoutEnv), 0);
+    }
+    OE_DEBUG << LC << "Setting timeout to " << timeout << std::endl;
+    curl_easy_setopt( _curl_handle, CURLOPT_TIMEOUT, timeout );
 
     _initialized = true;
 }
@@ -370,17 +390,27 @@ HTTPClient::~HTTPClient()
 void
 HTTPClient::setProxySettings( const ProxySettings& proxySettings )
 {
-    _proxySettings = proxySettings;
+    s_proxySettings = proxySettings;
 }
 
 const std::string& HTTPClient::getUserAgent()
 {
-    return _userAgent;
+    return s_userAgent;
 }
 
 void  HTTPClient::setUserAgent(const std::string& userAgent)
 {
-    _userAgent = userAgent;
+    s_userAgent = userAgent;
+}
+
+long HTTPClient::getTimeout()
+{
+    return s_timeout;
+}
+
+void HTTPClient::setTimeout( long timeout )
+{
+    s_timeout = timeout;
 }
 
 void
@@ -580,8 +610,6 @@ HTTPClient::doGet( const HTTPRequest& request, const osgDB::Options* options, Pr
 {
     initialize();
 
-    OE_TEST << LC << "doGet " << request.getURL() << std::endl;
-
     const osgDB::AuthenticationMap* authenticationMap = (options && options->getAuthenticationMap()) ? 
             options->getAuthenticationMap() :
             osgDB::Registry::instance()->getAuthenticationMap();
@@ -595,15 +623,15 @@ HTTPClient::doGet( const HTTPRequest& request, const osgDB::Options* options, Pr
     // the proxy information changes.
 
     //Try to get the proxy settings from the global settings
-    if (_proxySettings.isSet())
+    if (s_proxySettings.isSet())
     {
-        proxy_host = _proxySettings.get().hostName();
+        proxy_host = s_proxySettings.get().hostName();
         std::stringstream buf;
-        buf << _proxySettings.get().port();
+        buf << s_proxySettings.get().port();
         proxy_port = buf.str();
 
-        std::string proxy_username = _proxySettings.get().userName();
-        std::string proxy_password = _proxySettings.get().password();
+        std::string proxy_username = s_proxySettings.get().userName();
+        std::string proxy_password = s_proxySettings.get().password();
         if (!proxy_username.empty() && !proxy_password.empty())
         {
             proxy_auth = proxy_username + std::string(":") + proxy_password;
@@ -651,14 +679,18 @@ HTTPClient::doGet( const HTTPRequest& request, const osgDB::Options* options, Pr
         bufStr = buf.str();
         proxy_addr = bufStr;
     
-        OE_DEBUG << LC << "setting proxy: " << proxy_addr << std::endl;
+        if ( s_HTTP_DEBUG )
+            OE_NOTICE << LC << "Using proxy: " << proxy_addr << std::endl;
+
         //curl_easy_setopt( _curl_handle, CURLOPT_HTTPPROXYTUNNEL, 1 ); 
         curl_easy_setopt( _curl_handle, CURLOPT_PROXY, proxy_addr.c_str() );
 
         //Setup the proxy authentication if setup
         if (!proxy_auth.empty())
         {
-            OE_DEBUG << LC << "Setting up proxy authentication " << proxy_auth << std::endl;
+            if ( s_HTTP_DEBUG )
+                OE_NOTICE << LC << "Using proxy authentication " << proxy_auth << std::endl;
+
             curl_easy_setopt( _curl_handle, CURLOPT_PROXYUSERPWD, proxy_auth.c_str());
         }
     }
@@ -740,8 +772,12 @@ HTTPClient::doGet( const HTTPRequest& request, const osgDB::Options* options, Pr
         if (!proxy_addr.empty())
         {
             long connect_code = 0L;
-            curl_easy_getinfo( _curl_handle, CURLINFO_HTTP_CONNECTCODE, &connect_code );
-            OE_DEBUG << LC << "proxy connect code " << connect_code << std::endl;
+            CURLcode r = curl_easy_getinfo(_curl_handle, CURLINFO_HTTP_CONNECTCODE, &connect_code);
+            if ( r != CURLE_OK )
+            {
+                OE_WARN << LC << "Proxy connect error: " << curl_easy_strerror(r) << std::endl;
+                return HTTPResponse(0);
+            }
         }
         
         curl_easy_getinfo( _curl_handle, CURLINFO_RESPONSE_CODE, &response_code );
@@ -753,21 +789,24 @@ HTTPClient::doGet( const HTTPRequest& request, const osgDB::Options* options, Pr
         res = response_code == 408 ? CURLE_OPERATION_TIMEDOUT : CURLE_COULDNT_CONNECT;
     }
 
-    //OE_DEBUG << LC << "got response, code = " << response_code << std::endl;
+    if ( s_HTTP_DEBUG )
+    {
+        OE_NOTICE << LC << "GET(" << response_code << "): \"" << request.getURL() << "\"" << std::endl;
+    }
 
     HTTPResponse response( response_code );
    
-    if ( response_code == 200L && res != CURLE_ABORTED_BY_CALLBACK && res != CURLE_OPERATION_TIMEDOUT ) //res == 0 )
+    if ( response_code == 200L && res != CURLE_ABORTED_BY_CALLBACK && res != CURLE_OPERATION_TIMEDOUT )
     {
         // check for multipart content:
         char* content_type_cp;
         curl_easy_getinfo( _curl_handle, CURLINFO_CONTENT_TYPE, &content_type_cp );
         if ( content_type_cp == NULL )
         {
-            OE_NOTICE << LC
+            OE_WARN << LC
                 << "NULL Content-Type (protocol violation) " 
                 << "URL=" << request.getURL() << std::endl;
-            return NULL;
+            return HTTPResponse(0L);
         }
 
         // NOTE:
@@ -795,7 +834,7 @@ HTTPClient::doGet( const HTTPRequest& request, const osgDB::Options* options, Pr
         }
     }
     else if (res == CURLE_ABORTED_BY_CALLBACK || res == CURLE_OPERATION_TIMEDOUT)
-    {
+    {        
         //If we were aborted by a callback, then it was cancelled by a user
         response._cancelled = true;
     }
@@ -929,7 +968,7 @@ HTTPClient::doReadImage(const std::string&    location,
 
         //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
         if (HTTPClient::isRecoverable( result.code() ) )
-        {
+        {            
             if (callback)
             {
                 OE_DEBUG << "Error in HTTPClient for " << location << " but it's recoverable" << std::endl;
@@ -1090,7 +1129,7 @@ HTTPClient::doReadString(const std::string&    location,
 
         //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
         if (HTTPClient::isRecoverable( result.code() ) )
-        {
+        {            
             if (callback)
             {
                 OE_DEBUG << "Error in HTTPClient for " << location << " but it's recoverable" << std::endl;
diff --git a/src/osgEarth/ImageLayer.cpp b/src/osgEarth/ImageLayer.cpp
index d4d036f..bbf36a3 100644
--- a/src/osgEarth/ImageLayer.cpp
+++ b/src/osgEarth/ImageLayer.cpp
@@ -451,7 +451,7 @@ ImageLayer::createImageInNativeProfile( const TileKey& key, ProgressCallback* pr
             GeoExtent tightExtent = nativeProfile->clampAndTransformExtent( key.getExtent() );
 
             // a non-exact crop is critical here to avoid resampling the data
-            return result.crop( tightExtent, false );
+            return result.crop( tightExtent, false, 0, 0, *_runtimeOptions.driver()->bilinearReprojection() );
         }
 
         else // all fallback data
@@ -670,7 +670,7 @@ ImageLayer::createImageFromTileSource(const TileKey&    key,
                         // same pixel size; because chances are if we're requesting a fallback that we're
                         // planning to mosaic it later, and the mosaicer requires same-size images.
                         GeoImage raw( result.get(), finalKey.getExtent() );
-                        GeoImage cropped = raw.crop( key.getExtent(), true, raw.getImage()->s(), raw.getImage()->t() );
+                        GeoImage cropped = raw.crop( key.getExtent(), true, raw.getImage()->s(), raw.getImage()->t(), *_runtimeOptions.driver()->bilinearReprojection() );
                         result = cropped.takeImage();
                         fellBack = true;
                     }
@@ -811,7 +811,8 @@ ImageLayer::assembleImageFromTileSource(const TileKey&    key,
             key.getProfile()->getSRS(),
             &key.getExtent(), 
             *_runtimeOptions.reprojectedTileSize(),
-            *_runtimeOptions.reprojectedTileSize() );
+            *_runtimeOptions.reprojectedTileSize(),
+            *_runtimeOptions.driver()->bilinearReprojection());
     }
 
     return result;
diff --git a/src/osgEarth/Locators.cpp b/src/osgEarth/Locators.cpp
index 8138970..b5116f3 100644
--- a/src/osgEarth/Locators.cpp
+++ b/src/osgEarth/Locators.cpp
@@ -19,7 +19,7 @@
 #include <osgEarth/Locators>
 #include <osgEarth/TileKey>
 #include <osgEarth/Registry>
-#include <osgEarth/Map>
+#include <osgEarth/MapInfo>
 #include <osg/Notify>
 
 using namespace osgEarth;
diff --git a/src/osgEarth/Map b/src/osgEarth/Map
index d1b1bfd..db46ef8 100644
--- a/src/osgEarth/Map
+++ b/src/osgEarth/Map
@@ -24,6 +24,7 @@
 #include <osgEarth/GeoData>
 #include <osgEarth/Profile>
 #include <osgEarth/MapOptions>
+#include <osgEarth/MapCallback>
 #include <osgEarth/ImageLayer>
 #include <osgEarth/ElevationLayer>
 #include <osgEarth/ModelLayer>
@@ -33,80 +34,10 @@
 #include <osgDB/ReaderWriter>
 
 namespace osgEarth
-{    
+{
     class MapInfo;
 
     /**
-     * Conveys atomic change actions to the map data model.
-     */
-    struct MapModelChange
-    {
-        enum ActionType {
-            ADD_IMAGE_LAYER,
-            REMOVE_IMAGE_LAYER,
-            MOVE_IMAGE_LAYER,
-            ADD_ELEVATION_LAYER,
-            REMOVE_ELEVATION_LAYER,
-            MOVE_ELEVATION_LAYER,
-            ADD_MODEL_LAYER,
-            REMOVE_MODEL_LAYER,
-            MOVE_MODEL_LAYER,
-            ADD_MASK_LAYER,
-            REMOVE_MASK_LAYER,
-            UNSPECIFIED
-        };
-
-        MapModelChange( ActionType action, Revision mapModeRev, Layer* layer, int firstIndex =-1, int secondIndex =-1 ) 
-            : _action(action), _layer(layer), _modelRevision(mapModeRev), _firstIndex(firstIndex), _secondIndex(secondIndex) { }
-
-        const ActionType& getAction() const { return _action; }
-        const Revision& getRevision() const { return _modelRevision; }
-        int getFirstIndex() const { return _firstIndex; }
-        int getSecondIndex() const { return _secondIndex; }
-        Layer* getLayer() const { return _layer.get(); }
-        ImageLayer* getImageLayer() const { return dynamic_cast<ImageLayer*>(_layer.get()); }
-        ElevationLayer* getElevationLayer() const { return dynamic_cast<ElevationLayer*>(_layer.get()); }
-        ModelLayer* getModelLayer() const { return dynamic_cast<ModelLayer*>(_layer.get()); }
-        MaskLayer* getMaskLayer() const { return dynamic_cast<MaskLayer*>(_layer.get()); }
-
-    private:
-        ActionType _action;
-        osg::ref_ptr<Layer> _layer;
-        Revision _modelRevision;
-        int _firstIndex, _secondIndex;
-    };
-
-    /**
-     * Callback that the Map object uses to notify listeners of map data model changes.
-     */
-    struct OSGEARTH_EXPORT MapCallback : public osg::Referenced
-    {
-        virtual void onMapInfoEstablished( const MapInfo& mapInfo ) { } 
-
-        virtual void onMapModelChanged( const MapModelChange& change );
-
-        virtual void onImageLayerAdded( ImageLayer* layer, unsigned int index ) { }
-        virtual void onImageLayerRemoved( ImageLayer* layer, unsigned int index ) { }
-        virtual void onImageLayerMoved( ImageLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { }
-
-        virtual void onElevationLayerAdded( ElevationLayer* layer, unsigned int index ) { }
-        virtual void onElevationLayerRemoved( ElevationLayer* layer, unsigned int index ) { }
-        virtual void onElevationLayerMoved( ElevationLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { }
-
-        virtual void onModelLayerAdded( ModelLayer* layer, unsigned int index ) { }
-        virtual void onModelLayerRemoved( ModelLayer* layer ) { }
-        virtual void onModelLayerMoved( ModelLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { }
-
-        virtual void onMaskLayerAdded( MaskLayer* mask ) { }
-        virtual void onMaskLayerRemoved( MaskLayer* mask ) { }
-
-        /** dtor */
-        virtual ~MapCallback() { }
-    };
-
-    typedef std::list< osg::ref_ptr<MapCallback> > MapCallbackList;
-
-    /**
      * Map is the main data model that the MapNode will render. It is a
      * container for all Layer objects (that contain the actual data) and
      * the rendering options.
@@ -442,133 +373,6 @@ namespace osgEarth
 
         friend class MapInfo;
     };
-
-
-    /**
-     * A convenience class that combines a general geospatial profile and additional 
-     * information about the map itself.
-     */
-    class OSGEARTH_EXPORT MapInfo
-    {
-    public:
-        MapInfo( const Map* map );
-
-        MapInfo( const MapInfo& rhs );
-
-        /** dtor */
-        virtual ~MapInfo() { }
-        
-        const Profile* getProfile() const { return _profile.get(); }
-        const SpatialReference* getSRS() const { return _profile->getSRS(); }
-
-        bool isGeocentric() const { return _isGeocentric; }
-        bool isCube() const { return _isCube; }
-        bool isPlateCarre() const { return _profile->getSRS()->isPlateCarre(); }
-
-        bool isProjectedSRS() const { return !isGeographicSRS(); }
-        bool isGeographicSRS() const { return _profile->getSRS()->isGeographic(); }        
-
-        ElevationInterpolation getElevationInterpolation() const { return _elevationInterpolation;}
-
-    private:
-        osg::ref_ptr<const Profile> _profile;
-        bool _isGeocentric, _isCube;
-        ElevationInterpolation _elevationInterpolation;
-    };
-
-
-    /**
-     * A "snapshot in time" of a Map model revision. Use this class to get a safe "copy" of
-     * the map model lists that you can use without worrying about the model changing underneath
-     * you from another thread.
-     */
-    class OSGEARTH_EXPORT MapFrame
-    {
-    public:
-        /**
-         * Constructs a new map frame.
-         */
-        MapFrame( const Map* map, Map::ModelParts parts =Map::TERRAIN_LAYERS, const std::string& name ="" );
-
-        /**
-         * A copy constructor with a new name (no sync happens)
-         */
-        MapFrame( const MapFrame& frame, const std::string& name ="" );
-
-        /** dtor */
-        virtual ~MapFrame() { }
-
-        /**
-         * Synchronizes this frame with the source map model (only if necessary). Returns
-         * true is new data was synced; false if nothing changed.
-         */
-        bool sync();
-
-        /** Accesses the profile/rendering info about the source map. */
-        const MapInfo& getMapInfo() const { return _mapInfo; }
-
-        /** Convenience method to access the map's profile */
-        const Profile* getProfile() const { return _mapInfo.getProfile(); }
-        
-
-        /** The image layer stack snapshot */
-        const ImageLayerVector& imageLayers() const { return _imageLayers; }
-        ImageLayer* getImageLayerAt( int index ) const { return _imageLayers[index].get(); }
-        ImageLayer* getImageLayerByUID( UID uid ) const;
-        ImageLayer* getImageLayerByName( const std::string& name ) const;
-
-        /** The elevation layer stack snapshot */
-        const ElevationLayerVector& elevationLayers() const { return _elevationLayers; }
-        ElevationLayer* getElevationLayerAt( int index ) const { return _elevationLayers[index].get(); }
-        ElevationLayer* getElevationLayerByUID( UID uid ) const;
-        ElevationLayer* getElevationLayerByName( const std::string& name ) const;
-
-        /** The model layer set snapshot */
-        const ModelLayerVector& modelLayers() const { return _modelLayers; }
-        ModelLayer* getModelLayerAt(int index) const { return _modelLayers[index].get(); }
-
-        /** The mask layer set snapshot */
-        const MaskLayerVector& terrainMaskLayers() const { return _maskLayers; }
-
-        /** Gets the index of the layer in the layer stack snapshot. */
-        int indexOf( ImageLayer* layer ) const;
-        int indexOf( ElevationLayer* layer ) const;
-        int indexOf( ModelLayer* layer ) const;
-
-        /** Gets the map data model revision with which this frame is currently sync'd */
-        Revision getRevision() const { return _mapDataModelRevision; }
-
-        /** Checks whether all the data for the specified key is cached. */
-        bool isCached( const TileKey& key ) const;
-
-        /**
-         * Equivalent to the Map::createHeightField() method, but operates on the elevation stack
-         * snapshot in this MapFrame.
-         */
-        bool getHeightField(
-            const TileKey&                  key,
-            bool                            fallback,
-            osg::ref_ptr<osg::HeightField>& out_hf,
-            bool*                           out_isFallback =0L,   
-            bool                            convertToHAE   =true,         
-            ElevationSamplePolicy           samplePolicy   =SAMPLE_FIRST_VALID,
-            ProgressCallback*               progress       =0L ) const;
-
-    private:
-        bool _initialized;
-        osg::observer_ptr<const Map> _map;
-        //osg::ref_ptr<const Map> _map;
-        std::string _name;
-        MapInfo _mapInfo;
-        Map::ModelParts _parts;
-        Revision _mapDataModelRevision;
-        ImageLayerVector _imageLayers;
-        ElevationLayerVector _elevationLayers;
-        ModelLayerVector _modelLayers;
-        MaskLayerVector _maskLayers;
-        friend class Map;        
-    };
-
 }
 
 #endif // OSGEARTH_MAP_H
diff --git a/src/osgEarth/Map.cpp b/src/osgEarth/Map.cpp
index 1165ac4..217e2cb 100644
--- a/src/osgEarth/Map.cpp
+++ b/src/osgEarth/Map.cpp
@@ -17,6 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarth/Map>
+#include <osgEarth/MapFrame>
+#include <osgEarth/MapModelChange>
 #include <osgEarth/Registry>
 #include <osgEarth/TileSource>
 #include <osgEarth/HeightFieldUtils>
@@ -29,40 +31,6 @@ using namespace osgEarth;
 
 //------------------------------------------------------------------------
 
-void
-MapCallback::onMapModelChanged( const MapModelChange& change )
-{
-    switch( change.getAction() )
-    {
-    case MapModelChange::ADD_ELEVATION_LAYER: 
-        onElevationLayerAdded( change.getElevationLayer(), change.getFirstIndex() ); break;
-    case MapModelChange::ADD_IMAGE_LAYER:
-        onImageLayerAdded( change.getImageLayer(), change.getFirstIndex() ); break;
-    case MapModelChange::ADD_MASK_LAYER:
-        onMaskLayerAdded( change.getMaskLayer() ); break;
-    case MapModelChange::ADD_MODEL_LAYER:
-        onModelLayerAdded( change.getModelLayer(), change.getFirstIndex() ); break;
-    case MapModelChange::REMOVE_ELEVATION_LAYER:
-        onElevationLayerRemoved( change.getElevationLayer(), change.getFirstIndex() ); break;
-    case MapModelChange::REMOVE_IMAGE_LAYER:
-        onImageLayerRemoved( change.getImageLayer(), change.getFirstIndex() ); break;
-    case MapModelChange::REMOVE_MASK_LAYER:
-        onMaskLayerRemoved( change.getMaskLayer() ); break;
-    case MapModelChange::REMOVE_MODEL_LAYER:
-        onModelLayerRemoved( change.getModelLayer() ); break;
-    case MapModelChange::MOVE_ELEVATION_LAYER:
-        onElevationLayerMoved( change.getElevationLayer(), change.getFirstIndex(), change.getSecondIndex() ); break;
-    case MapModelChange::MOVE_IMAGE_LAYER:
-        onImageLayerMoved( change.getImageLayer(), change.getFirstIndex(), change.getSecondIndex() ); break;
-    case MapModelChange::MOVE_MODEL_LAYER:
-            onModelLayerMoved( change.getModelLayer(), change.getFirstIndex(), change.getSecondIndex() ); break;
-    case MapModelChange::UNSPECIFIED: break;
-    default: break;
-    }
-}
-
-//------------------------------------------------------------------------
-
 Map::Map( const MapOptions& options ) :
 osg::Referenced      ( true ),
 _mapOptions          ( options ),
@@ -77,7 +45,8 @@ _dataModelRevision   ( 0 )
 
     // if the map was a cache policy set, make this the system-wide default, UNLESS
     // there ALREADY IS a registry default, in which case THAT will override THIS one.
-    // (In other words, whichever one is set first wins.)
+    // (In other words, whichever one is set first wins. And of course, if the registry
+    // has an override set, that will cancel out all of this.)
     const optional<CachePolicy> regCachePolicy = Registry::instance()->defaultCachePolicy();
 
     if ( _mapOptions.cachePolicy().isSet() )
@@ -1050,257 +1019,6 @@ Map::calculateProfile()
     }
 }
 
-namespace
-{
-    /**
-     * Returns a heightfield corresponding to the input key by compositing
-     * elevation data for a vector of elevation layers. The resulting 
-     * heightfield's height values will be expressed relative to the vertical
-     * datum in the requesting key (which is usually that of the Map itself).
-     */
-    bool
-    s_getHeightField(const TileKey&                  key,
-                     const ElevationLayerVector&     elevLayers,
-                     bool                            fallback,
-                     const Profile*                  haeProfile,
-                     ElevationInterpolation          interpolation,
-                     ElevationSamplePolicy           samplePolicy,
-                     osg::ref_ptr<osg::HeightField>& out_result,
-                     bool*                           out_isFallback,
-                     ProgressCallback*               progress ) 
-    {        
-        unsigned lowestLOD = key.getLevelOfDetail();
-        bool hfInitialized = false;
-
-        //Get a HeightField for each of the enabled layers
-        GeoHeightFieldVector heightFields;
-        
-        //The number of fallback heightfields we have
-        int numFallbacks = 0;
-
-        //Default to being fallback data.
-        if ( out_isFallback )
-        {
-            *out_isFallback = true;
-        }
-
-        // if the caller provided an "HAE map profile", he wants an HAE elevation grid even if
-        // the map profile has a vertical datum. This is the usual case when building the 3D
-        // terrain, for example. Construct a temporary key that doesn't have the vertical
-        // datum info and use that to query the elevation data.
-        TileKey keyToUse = key;
-        if ( haeProfile )
-        {
-            keyToUse = TileKey(key.getLevelOfDetail(), key.getTileX(), key.getTileY(), haeProfile );
-        }
-
-        // Generate a heightfield for each elevation layer.
-
-        unsigned defElevSize = 8;
-
-        for( ElevationLayerVector::const_iterator i = elevLayers.begin(); i != elevLayers.end(); i++ )
-        {
-            ElevationLayer* layer = i->get();
-            if ( layer->getVisible() )
-            {
-                GeoHeightField geoHF = layer->createHeightField( keyToUse, progress );
-
-                // if "fallback" is set, try to fall back on lower LODs.
-                if ( !geoHF.valid() && fallback )
-                {
-                    TileKey hf_key = keyToUse.createParentKey();
-
-                    while ( hf_key.valid() && !geoHF.valid() )
-                    {
-                        geoHF = layer->createHeightField( hf_key, progress );
-                        if ( !geoHF.valid() )
-                            hf_key = hf_key.createParentKey();
-                    }
-
-                    if ( geoHF.valid() )
-                    {
-                        if ( hf_key.getLevelOfDetail() < lowestLOD )
-                            lowestLOD = hf_key.getLevelOfDetail();
-
-                        //This HeightField is fallback data, so increment the count.
-                        numFallbacks++;                        
-                    }
-                }
-
-                if ( geoHF.valid() )
-                {
-                    heightFields.push_back( geoHF );
-                }
-            }
-        }
-
-        //If any of the layers produced valid data then it's not considered a fallback
-        if ( out_isFallback )
-        {
-            *out_isFallback = (numFallbacks == heightFields.size());
-            //OE_NOTICE << "Num fallbacks=" << numFallbacks << " numHeightFields=" << heightFields.size() << " is fallback " << *out_isFallback << std::endl;
-        }   
-        
-        if ( heightFields.size() == 0 )
-        {            
-            //If we got no heightfields but were requested to fallback, create an empty heightfield.
-            if ( fallback )
-            {
-                out_result = HeightFieldUtils::createReferenceHeightField( keyToUse.getExtent(), defElevSize, defElevSize );                
-                return true;
-            }
-            else
-            {
-                //We weren't requested to fallback so just return.
-                return false;
-            }
-        }
-
-        else if (heightFields.size() == 1)
-        {
-            if ( lowestLOD == key.getLevelOfDetail() )
-            {
-                //If we only have on heightfield, just return it.
-                out_result = heightFields[0].takeHeightField();
-            }
-            else
-            {
-                GeoHeightField geoHF = heightFields[0].createSubSample( key.getExtent(), interpolation);
-                out_result = geoHF.takeHeightField();
-                hfInitialized = true;
-            }
-        }
-
-        else
-        {
-            //If we have multiple heightfields, we need to composite them together.
-            unsigned int width = 0;
-            unsigned int height = 0;
-
-            for (GeoHeightFieldVector::const_iterator i = heightFields.begin(); i < heightFields.end(); ++i)
-            {
-                if (i->getHeightField()->getNumColumns() > width) 
-                    width = i->getHeightField()->getNumColumns();
-                if (i->getHeightField()->getNumRows() > height) 
-                    height = i->getHeightField()->getNumRows();
-            }
-            out_result = new osg::HeightField();
-            out_result->allocate( width, height );
-
-            //Go ahead and set up the heightfield so we don't have to worry about it later
-            double minx, miny, maxx, maxy;
-            key.getExtent().getBounds(minx, miny, maxx, maxy);
-            double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
-            double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);
-
-            const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();
-            
-            //Create the new heightfield by sampling all of them.
-            for (unsigned int c = 0; c < width; ++c)
-            {
-                double x = minx + (dx * (double)c);
-                for (unsigned r = 0; r < height; ++r)
-                {
-                    double y = miny + (dy * (double)r);
-
-                    //Collect elevations from all of the layers. Iterate BACKWARDS because the last layer
-                    // is the highest priority.
-                    std::vector<float> elevations;
-                    for( GeoHeightFieldVector::reverse_iterator itr = heightFields.rbegin(); itr != heightFields.rend(); ++itr )
-                    {
-                        const GeoHeightField& geoHF = *itr;
-
-                        float elevation = 0.0f;
-                        if ( geoHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) )
-                        {
-                            if (elevation != NO_DATA_VALUE)
-                            {
-                                elevations.push_back(elevation);
-                            }
-                        }
-                    }
-
-                    float elevation = NO_DATA_VALUE;
-
-                    //The list of elevations only contains valid values
-                    if (elevations.size() > 0)
-                    {
-                        if (samplePolicy == SAMPLE_FIRST_VALID)
-                        {
-                            elevation = elevations[0];
-                        }
-                        else if (samplePolicy == SAMPLE_HIGHEST)
-                        {
-                            elevation = -FLT_MAX;
-                            for (unsigned int i = 0; i < elevations.size(); ++i)
-                            {
-                                if (elevation < elevations[i]) elevation = elevations[i];
-                            }
-                        }
-                        else if (samplePolicy == SAMPLE_LOWEST)
-                        {
-                            elevation = FLT_MAX;
-                            for (unsigned i = 0; i < elevations.size(); ++i)
-                            {
-                                if (elevation > elevations[i]) elevation = elevations[i];
-                            }
-                        }
-                        else if (samplePolicy == SAMPLE_AVERAGE)
-                        {
-                            elevation = 0.0;
-                            for (unsigned i = 0; i < elevations.size(); ++i)
-                            {
-                                elevation += elevations[i];
-                            }
-                            elevation /= (float)elevations.size();
-                        }
-                    }
-                    out_result->setHeight(c, r, elevation);
-                }
-            }
-        }
-
-        // Replace any NoData areas with the reference value. This is zero for HAE datums,
-        // and some geoid height for orthometric datums.
-        if (out_result.valid())
-        {
-            const Geoid*         geoid = 0L;
-            const VerticalDatum* vdatum = key.getProfile()->getSRS()->getVerticalDatum();
-
-            if ( haeProfile && vdatum )
-            {
-                geoid = vdatum->getGeoid();
-            }
-
-            HeightFieldUtils::resolveInvalidHeights(
-                out_result.get(),
-                key.getExtent(),
-                NO_DATA_VALUE,
-                geoid );
-
-            //ReplaceInvalidDataOperator o;
-            //o.setValidDataOperator(new osgTerrain::NoDataValue(NO_DATA_VALUE));
-            //o( out_result.get() );
-        }
-
-        //Initialize the HF values for osgTerrain
-        if (out_result.valid() && !hfInitialized )
-        {   
-            //Go ahead and set up the heightfield so we don't have to worry about it later
-            double minx, miny, maxx, maxy;
-            key.getExtent().getBounds(minx, miny, maxx, maxy);
-            out_result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) );
-            double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
-            double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);
-            out_result->setXInterval( dx );
-            out_result->setYInterval( dy );
-            out_result->setBorderWidth( 0 );
-        }
-
-        return out_result.valid();
-    }
-}
-
 
 bool
 Map::getHeightField(const TileKey&                  key,
@@ -1315,9 +1033,8 @@ Map::getHeightField(const TileKey&                  key,
 
     ElevationInterpolation interp = getMapOptions().elevationInterpolation().get();    
 
-    return s_getHeightField(
+    return _elevationLayers.createHeightField(
         key, 
-        _elevationLayers, 
         fallback, 
         convertToHAE ? _profileNoVDatum.get() : 0L,
         interp, 
@@ -1377,176 +1094,3 @@ Map::sync( MapFrame& frame ) const
     }    
     return result;
 }
-
-//------------------------------------------------------------------------
-
-MapInfo::MapInfo( const Map* map ) :
-_profile               ( 0L ),
-_isGeocentric          ( true ),
-_isCube                ( false ),
-_elevationInterpolation( INTERP_BILINEAR )
-{ 
-    if ( map )
-    {
-        _profile = map->getProfile();
-        _isGeocentric = map->isGeocentric();
-        _isCube = map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
-        _elevationInterpolation = *map->getMapOptions().elevationInterpolation();
-    }
-}
-
-MapInfo::MapInfo( const MapInfo& rhs ) :
-_profile               ( rhs._profile ),
-_isGeocentric          ( rhs._isGeocentric ),
-_isCube                ( rhs._isCube ),
-_elevationInterpolation( rhs._elevationInterpolation )
-{
-    //nop
-}              
-
-//------------------------------------------------------------------------
-
-MapFrame::MapFrame( const Map* map, Map::ModelParts parts, const std::string& name ) :
-_initialized( false ),
-_map        ( map ),
-_name       ( name ),
-_mapInfo    ( map ),
-_parts      ( parts )
-{
-    sync();
-}
-
-MapFrame::MapFrame( const MapFrame& src, const std::string& name ) :
-_initialized         ( src._initialized ),
-_map                 ( src._map.get() ),
-_name                ( name ),
-_mapInfo             ( src._mapInfo ),
-_parts               ( src._parts ),
-_mapDataModelRevision( src._mapDataModelRevision ),
-_imageLayers         ( src._imageLayers ),
-_elevationLayers     ( src._elevationLayers ),
-_modelLayers         ( src._modelLayers ),
-_maskLayers          ( src._maskLayers )
-{
-    //no sync required here; we copied the arrays etc
-}
-
-bool
-MapFrame::sync()
-{
-    if ( _map.valid() )
-    {
-        return _map->sync( *this );
-    }
-    else
-    {
-        _imageLayers.clear();
-        _elevationLayers.clear();
-        _modelLayers.clear();
-        _maskLayers.clear();
-        return false;
-    }
-}
-
-bool
-MapFrame::getHeightField(const TileKey&                  key,
-                         bool                            fallback,
-                         osg::ref_ptr<osg::HeightField>& out_hf,
-                         bool*                           out_isFallback,    
-                         bool                            convertToHAE,
-                         ElevationSamplePolicy           samplePolicy,
-                         ProgressCallback*               progress) const
-{
-    if ( !_map.valid() ) return false;
-
-    return s_getHeightField( 
-        key, 
-        _elevationLayers,
-        fallback, 
-        convertToHAE ? _map->getProfileNoVDatum() : 0L,
-        _mapInfo.getElevationInterpolation(), 
-        samplePolicy, 
-        out_hf, 
-        out_isFallback,
-        progress );
-}
-
-int
-MapFrame::indexOf( ImageLayer* layer ) const
-{
-    ImageLayerVector::const_iterator i = std::find( _imageLayers.begin(), _imageLayers.end(), layer );
-    return i != _imageLayers.end() ? i - _imageLayers.begin() : -1;
-}
-
-int
-MapFrame::indexOf( ElevationLayer* layer ) const
-{
-    ElevationLayerVector::const_iterator i = std::find( _elevationLayers.begin(), _elevationLayers.end(), layer );
-    return i != _elevationLayers.end() ? i - _elevationLayers.begin() : -1;
-}
-
-int
-MapFrame::indexOf( ModelLayer* layer ) const
-{
-    ModelLayerVector::const_iterator i = std::find( _modelLayers.begin(), _modelLayers.end(), layer );
-    return i != _modelLayers.end() ? i - _modelLayers.begin() : -1;
-}
-
-ImageLayer*
-MapFrame::getImageLayerByUID( UID uid ) const
-{
-    for(ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i )
-        if ( i->get()->getUID() == uid )
-            return i->get();
-    return 0L;
-}
-
-ImageLayer*
-MapFrame::getImageLayerByName( const std::string& name ) const
-{
-    for(ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i )
-        if ( i->get()->getName() == name )
-            return i->get();
-    return 0L;
-}
-
-bool
-MapFrame::isCached( const TileKey& key ) const
-{
-    //Check to see if the tile will load fast
-    // Check the imagery layers
-    for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ )
-    {   
-        //If we're cache only we should be fast
-        if (i->get()->isCacheOnly()) continue;
-
-        osg::ref_ptr< TileSource > source = i->get()->getTileSource();
-        if (!source.valid()) continue;
-
-        //If the tile is blacklisted, it should also be fast.
-        if ( source->getBlacklist()->contains( key.getTileId() ) ) continue;
-        //If no data is available on this tile, we'll be fast
-        if ( !source->hasData( key ) ) continue;
-
-        if ( !i->get()->isCached( key ) ) return false;
-    }
-
-    for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i )
-    {
-        //If we're cache only we should be fast
-        if (i->get()->isCacheOnly()) continue;
-
-        osg::ref_ptr< TileSource > source = i->get()->getTileSource();
-        if (!source.valid()) continue;
-
-        //If the tile is blacklisted, it should also be fast.
-        if ( source->getBlacklist()->contains( key.getTileId() ) ) continue;
-        if ( !source->hasData( key ) ) continue;
-        if ( !i->get()->isCached( key ) )
-        {
-            return false;
-        }
-    }
-
-    return true;        
-}
diff --git a/src/osgEarth/MapCallback b/src/osgEarth/MapCallback
new file mode 100644
index 0000000..21ad4b3
--- /dev/null
+++ b/src/osgEarth/MapCallback
@@ -0,0 +1,67 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef OSGEARTH_MAP_CALLBACK_H
+#define OSGEARTH_MAP_CALLBACK_H 1
+
+#include <osgEarth/Common>
+#include <osg/ref_ptr>
+#include <list>
+
+namespace osgEarth
+{
+    class MapInfo;
+    class ImageLayer;
+    class ElevationLayer;
+    class ModelLayer;
+    class MaskLayer;
+    struct MapModelChange;
+
+    /**
+     * Callback that the Map object uses to notify listeners of map data model changes.
+     */
+    struct OSGEARTH_EXPORT MapCallback : public osg::Referenced
+    {
+        virtual void onMapInfoEstablished( const MapInfo& mapInfo ) { } 
+
+        virtual void onMapModelChanged( const MapModelChange& change );
+
+        virtual void onImageLayerAdded( ImageLayer* layer, unsigned int index ) { }
+        virtual void onImageLayerRemoved( ImageLayer* layer, unsigned int index ) { }
+        virtual void onImageLayerMoved( ImageLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { }
+
+        virtual void onElevationLayerAdded( ElevationLayer* layer, unsigned int index ) { }
+        virtual void onElevationLayerRemoved( ElevationLayer* layer, unsigned int index ) { }
+        virtual void onElevationLayerMoved( ElevationLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { }
+
+        virtual void onModelLayerAdded( ModelLayer* layer, unsigned int index ) { }
+        virtual void onModelLayerRemoved( ModelLayer* layer ) { }
+        virtual void onModelLayerMoved( ModelLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { }
+
+        virtual void onMaskLayerAdded( MaskLayer* mask ) { }
+        virtual void onMaskLayerRemoved( MaskLayer* mask ) { }
+
+        /** dtor */
+        virtual ~MapCallback() { }
+    };
+
+    typedef std::list< osg::ref_ptr<MapCallback> > MapCallbackList;
+}
+
+#endif // OSGEARTH_MAP_CALLBACK_H
diff --git a/src/osgEarth/MapCallback.cpp b/src/osgEarth/MapCallback.cpp
new file mode 100644
index 0000000..bd44174
--- /dev/null
+++ b/src/osgEarth/MapCallback.cpp
@@ -0,0 +1,73 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#include <osgEarth/MapCallback>
+#include <osgEarth/MapModelChange>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ElevationLayer>
+#include <osgEarth/ModelLayer>
+#include <osgEarth/MaskLayer>
+
+#define LC "[MapCallback] "
+
+using namespace osgEarth;
+
+void
+MapCallback::onMapModelChanged( const MapModelChange& change )
+{
+    switch( change.getAction() )
+    {
+    case MapModelChange::ADD_ELEVATION_LAYER: 
+        onElevationLayerAdded( change.getElevationLayer(), change.getFirstIndex() );
+        break;
+    case MapModelChange::ADD_IMAGE_LAYER:
+        onImageLayerAdded( change.getImageLayer(), change.getFirstIndex() ); 
+        break;
+    case MapModelChange::ADD_MASK_LAYER:
+        onMaskLayerAdded( change.getMaskLayer() );
+        break;
+    case MapModelChange::ADD_MODEL_LAYER:
+        onModelLayerAdded( change.getModelLayer(), change.getFirstIndex() ); 
+        break;
+    case MapModelChange::REMOVE_ELEVATION_LAYER:
+        onElevationLayerRemoved( change.getElevationLayer(), change.getFirstIndex() ); 
+        break;
+    case MapModelChange::REMOVE_IMAGE_LAYER:
+        onImageLayerRemoved( change.getImageLayer(), change.getFirstIndex() );
+        break;
+    case MapModelChange::REMOVE_MASK_LAYER:
+        onMaskLayerRemoved( change.getMaskLayer() ); 
+        break;
+    case MapModelChange::REMOVE_MODEL_LAYER:
+        onModelLayerRemoved( change.getModelLayer() ); 
+        break;
+    case MapModelChange::MOVE_ELEVATION_LAYER:
+        onElevationLayerMoved( change.getElevationLayer(), change.getFirstIndex(), change.getSecondIndex() ); 
+        break;
+    case MapModelChange::MOVE_IMAGE_LAYER:
+        onImageLayerMoved( change.getImageLayer(), change.getFirstIndex(), change.getSecondIndex() ); 
+        break;
+    case MapModelChange::MOVE_MODEL_LAYER:
+        onModelLayerMoved( change.getModelLayer(), change.getFirstIndex(), change.getSecondIndex() ); 
+        break;
+    case MapModelChange::UNSPECIFIED: 
+        break;
+    default: 
+        break;
+    }
+}
diff --git a/src/osgEarth/MapFrame b/src/osgEarth/MapFrame
new file mode 100644
index 0000000..650a8ed
--- /dev/null
+++ b/src/osgEarth/MapFrame
@@ -0,0 +1,124 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef OSGEARTH_MAP_FRAME_H
+#define OSGEARTH_MAP_FRAME_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/Containers>
+#include <osgEarth/Map>
+#include <osgEarth/MapInfo>
+
+namespace osgEarth
+{
+    /**
+     * A "snapshot in time" of a Map model revision. Use this class to get a safe "copy" of
+     * the map model lists that you can use without worrying about the model changing underneath
+     * you from another thread.
+     */
+    class OSGEARTH_EXPORT MapFrame
+    {
+    public:
+        /**
+         * Constructs a new map frame.
+         */
+        MapFrame( const Map* map, Map::ModelParts parts =Map::TERRAIN_LAYERS, const std::string& name ="" );
+
+        /**
+         * A copy constructor with a new name (no sync happens)
+         */
+        MapFrame( const MapFrame& frame, const std::string& name ="" );
+
+        /** dtor */
+        virtual ~MapFrame() { }
+
+        /**
+         * Synchronizes this frame with the source map model (only if necessary). Returns
+         * true is new data was synced; false if nothing changed.
+         */
+        bool sync();
+
+        /** Accesses the profile/rendering info about the source map. */
+        const MapInfo& getMapInfo() const { return _mapInfo; }
+
+        /** Convenience method to access the map's profile */
+        const Profile* getProfile() const { return _mapInfo.getProfile(); }
+        
+
+        /** The image layer stack snapshot */
+        const ImageLayerVector& imageLayers() const { return _imageLayers; }
+        ImageLayer* getImageLayerAt( int index ) const { return _imageLayers[index].get(); }
+        ImageLayer* getImageLayerByUID( UID uid ) const;
+        ImageLayer* getImageLayerByName( const std::string& name ) const;
+
+        /** The elevation layer stack snapshot */
+        const ElevationLayerVector& elevationLayers() const { return _elevationLayers; }
+        ElevationLayer* getElevationLayerAt( int index ) const { return _elevationLayers[index].get(); }
+        ElevationLayer* getElevationLayerByUID( UID uid ) const;
+        ElevationLayer* getElevationLayerByName( const std::string& name ) const;
+
+        /** The model layer set snapshot */
+        const ModelLayerVector& modelLayers() const { return _modelLayers; }
+        ModelLayer* getModelLayerAt(int index) const { return _modelLayers[index].get(); }
+
+        /** The mask layer set snapshot */
+        const MaskLayerVector& terrainMaskLayers() const { return _maskLayers; }
+
+        /** Gets the index of the layer in the layer stack snapshot. */
+        int indexOf( ImageLayer* layer ) const;
+        int indexOf( ElevationLayer* layer ) const;
+        int indexOf( ModelLayer* layer ) const;
+
+        /** Gets the map data model revision with which this frame is currently sync'd */
+        Revision getRevision() const { return _mapDataModelRevision; }
+
+        /** Checks whether all the data for the specified key is cached. */
+        bool isCached( const TileKey& key ) const;
+
+        /**
+         * Equivalent to the Map::createHeightField() method, but operates on the elevation stack
+         * snapshot in this MapFrame.
+         */
+        bool getHeightField(
+            const TileKey&                  key,
+            bool                            fallback,
+            osg::ref_ptr<osg::HeightField>& out_hf,
+            bool*                           out_isFallback =0L,   
+            bool                            convertToHAE   =true,         
+            ElevationSamplePolicy           samplePolicy   =SAMPLE_FIRST_VALID,
+            ProgressCallback*               progress       =0L ) const;
+
+    private:
+        bool _initialized;
+        osg::observer_ptr<const Map> _map;
+        std::string _name;
+        MapInfo _mapInfo;
+        Map::ModelParts _parts;
+        Revision _mapDataModelRevision;
+        ImageLayerVector _imageLayers;
+        ElevationLayerVector _elevationLayers;
+        ModelLayerVector _modelLayers;
+        MaskLayerVector _maskLayers;        
+
+        friend class Map;
+    };
+
+}
+
+#endif // OSGEARTH_MAP_FRAME_H
diff --git a/src/osgEarth/MapFrame.cpp b/src/osgEarth/MapFrame.cpp
new file mode 100644
index 0000000..a39a9a8
--- /dev/null
+++ b/src/osgEarth/MapFrame.cpp
@@ -0,0 +1,183 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#include <osgEarth/MapFrame>
+
+using namespace osgEarth;
+
+#define LC "[MapFrame] "
+
+
+MapFrame::MapFrame( const Map* map, Map::ModelParts parts, const std::string& name ) :
+_initialized( false ),
+_map        ( map ),
+_name       ( name ),
+_mapInfo    ( map ),
+_parts      ( parts )
+{
+    sync();
+}
+
+
+MapFrame::MapFrame( const MapFrame& src, const std::string& name ) :
+_initialized         ( src._initialized ),
+_map                 ( src._map.get() ),
+_name                ( name ),
+_mapInfo             ( src._mapInfo ),
+_parts               ( src._parts ),
+_mapDataModelRevision( src._mapDataModelRevision ),
+_imageLayers         ( src._imageLayers ),
+_elevationLayers     ( src._elevationLayers ),
+_modelLayers         ( src._modelLayers ),
+_maskLayers          ( src._maskLayers )
+{
+    //no sync required here; we copied the arrays etc
+}
+
+
+bool
+MapFrame::sync()
+{
+    bool changed = false;
+
+    if ( _map.valid() )
+    {
+        changed = _map->sync( *this );        
+    }
+    else
+    {
+        _imageLayers.clear();
+        _elevationLayers.clear();
+        _modelLayers.clear();
+        _maskLayers.clear();        
+    }
+
+    return changed;
+}
+
+
+bool
+MapFrame::getHeightField(const TileKey&                  key,
+                         bool                            fallback,
+                         osg::ref_ptr<osg::HeightField>& out_hf,
+                         bool*                           out_isFallback,    
+                         bool                            convertToHAE,
+                         ElevationSamplePolicy           samplePolicy,
+                         ProgressCallback*               progress) const
+{
+    if ( !_map.valid() ) 
+        return false;
+    
+
+
+    return _elevationLayers.createHeightField(
+        key,
+        fallback, 
+        convertToHAE ? _map->getProfileNoVDatum() : 0L,
+        _mapInfo.getElevationInterpolation(), 
+        samplePolicy, 
+        out_hf, 
+        out_isFallback,
+        progress );    
+}
+
+
+int
+MapFrame::indexOf( ImageLayer* layer ) const
+{
+    ImageLayerVector::const_iterator i = std::find( _imageLayers.begin(), _imageLayers.end(), layer );
+    return i != _imageLayers.end() ? i - _imageLayers.begin() : -1;
+}
+
+
+int
+MapFrame::indexOf( ElevationLayer* layer ) const
+{
+    ElevationLayerVector::const_iterator i = std::find( _elevationLayers.begin(), _elevationLayers.end(), layer );
+    return i != _elevationLayers.end() ? i - _elevationLayers.begin() : -1;
+}
+
+
+int
+MapFrame::indexOf( ModelLayer* layer ) const
+{
+    ModelLayerVector::const_iterator i = std::find( _modelLayers.begin(), _modelLayers.end(), layer );
+    return i != _modelLayers.end() ? i - _modelLayers.begin() : -1;
+}
+
+
+ImageLayer*
+MapFrame::getImageLayerByUID( UID uid ) const
+{
+    for(ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i )
+        if ( i->get()->getUID() == uid )
+            return i->get();
+    return 0L;
+}
+
+
+ImageLayer*
+MapFrame::getImageLayerByName( const std::string& name ) const
+{
+    for(ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i )
+        if ( i->get()->getName() == name )
+            return i->get();
+    return 0L;
+}
+
+
+bool
+MapFrame::isCached( const TileKey& key ) const
+{
+    //Check to see if the tile will load fast
+    // Check the imagery layers
+    for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ )
+    {   
+        //If we're cache only we should be fast
+        if (i->get()->isCacheOnly()) continue;
+
+        osg::ref_ptr< TileSource > source = i->get()->getTileSource();
+        if (!source.valid()) continue;
+
+        //If the tile is blacklisted, it should also be fast.
+        if ( source->getBlacklist()->contains( key.getTileId() ) ) continue;
+        //If no data is available on this tile, we'll be fast
+        if ( !source->hasData( key ) ) continue;
+
+        if ( !i->get()->isCached( key ) ) return false;
+    }
+
+    for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i )
+    {
+        //If we're cache only we should be fast
+        if (i->get()->isCacheOnly()) continue;
+
+        osg::ref_ptr< TileSource > source = i->get()->getTileSource();
+        if (!source.valid()) continue;
+
+        //If the tile is blacklisted, it should also be fast.
+        if ( source->getBlacklist()->contains( key.getTileId() ) ) continue;
+        if ( !source->hasData( key ) ) continue;
+        if ( !i->get()->isCached( key ) )
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
diff --git a/src/osgEarth/MapInfo b/src/osgEarth/MapInfo
new file mode 100644
index 0000000..b045e89
--- /dev/null
+++ b/src/osgEarth/MapInfo
@@ -0,0 +1,64 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef OSGEARTH_MAP_INFO_H
+#define OSGEARTH_MAP_INFO_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/GeoCommon>
+#include <osgEarth/Profile>
+
+namespace osgEarth
+{
+    class Map;
+
+    /**
+     * A convenience class that combines a general geospatial profile and additional 
+     * information about the map itself.
+     */
+    class OSGEARTH_EXPORT MapInfo
+    {
+    public:
+        MapInfo( const Map* map );
+
+        MapInfo( const MapInfo& rhs );
+
+        /** dtor */
+        virtual ~MapInfo() { }
+        
+        const Profile* getProfile() const { return _profile.get(); }
+        const SpatialReference* getSRS() const { return _profile->getSRS(); }
+
+        bool isGeocentric() const { return _isGeocentric; }
+        bool isCube() const { return _isCube; }
+        bool isPlateCarre() const { return _profile->getSRS()->isPlateCarre(); }
+
+        bool isProjectedSRS() const { return !isGeographicSRS(); }
+        bool isGeographicSRS() const { return _profile->getSRS()->isGeographic(); }
+
+        ElevationInterpolation getElevationInterpolation() const { return _elevationInterpolation;}
+
+    private:
+        osg::ref_ptr<const Profile> _profile;
+        bool _isGeocentric, _isCube;
+        ElevationInterpolation _elevationInterpolation;
+    };
+}
+
+#endif // OSGEARTH_MAP_CALLBACK_H
diff --git a/src/osgEarthFeatures/CentroidFilter.cpp b/src/osgEarth/MapInfo.cpp
similarity index 52%
copy from src/osgEarthFeatures/CentroidFilter.cpp
copy to src/osgEarth/MapInfo.cpp
index 0b89bd1..9284072 100644
--- a/src/osgEarthFeatures/CentroidFilter.cpp
+++ b/src/osgEarth/MapInfo.cpp
@@ -16,36 +16,34 @@
  * You should have received a copy of the GNU Lesser General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
-#include <osgEarthFeatures/CentroidFilter>
-
-#define LC "[CentroidFilter] "
+#include <osgEarth/MapInfo>
+#include <osgEarth/Map>
 
 using namespace osgEarth;
-using namespace osgEarth::Features;
 
-//------------------------------------------------------------------------
+#define LC "[MapInfo] "
 
-CentroidFilter::CentroidFilter()
-{
-    //NOP
-}
 
-FilterContext
-CentroidFilter::push(FeatureList& features, FilterContext& context )
-{
-    for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
+MapInfo::MapInfo( const Map* map ) :
+_profile               ( 0L ),
+_isGeocentric          ( true ),
+_isCube                ( false ),
+_elevationInterpolation( INTERP_BILINEAR )
+{ 
+    if ( map )
     {
-        Feature* f = i->get();
-        
-        Geometry* geom = f->getGeometry();
-        if ( !geom )
-            continue;
-
-        PointSet* newGeom = new PointSet();
-        newGeom->push_back( geom->getBounds().center() );
-
-        f->setGeometry( newGeom );
+        _profile = map->getProfile();
+        _isGeocentric = map->isGeocentric();
+        _isCube = map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
+        _elevationInterpolation = *map->getMapOptions().elevationInterpolation();
     }
+}
 
-    return context;
+MapInfo::MapInfo( const MapInfo& rhs ) :
+_profile               ( rhs._profile ),
+_isGeocentric          ( rhs._isGeocentric ),
+_isCube                ( rhs._isCube ),
+_elevationInterpolation( rhs._elevationInterpolation )
+{
+    //nop
 }
diff --git a/src/osgEarth/MapModelChange b/src/osgEarth/MapModelChange
new file mode 100644
index 0000000..5b92462
--- /dev/null
+++ b/src/osgEarth/MapModelChange
@@ -0,0 +1,74 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef OSGEARTH_MAP_MODEL_CHANGE_H
+#define OSGEARTH_MAP_MODEL_CHANGE_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/Revisioning>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ElevationLayer>
+#include <osgEarth/ModelLayer>
+#include <osgEarth/MaskLayer>
+
+namespace osgEarth
+{    
+    /**
+     * Conveys atomic change actions to the map data model.
+     */
+    struct MapModelChange /* no export, header-only */
+    {
+        enum ActionType
+        {
+            ADD_IMAGE_LAYER,
+            REMOVE_IMAGE_LAYER,
+            MOVE_IMAGE_LAYER,
+            ADD_ELEVATION_LAYER,
+            REMOVE_ELEVATION_LAYER,
+            MOVE_ELEVATION_LAYER,
+            ADD_MODEL_LAYER,
+            REMOVE_MODEL_LAYER,
+            MOVE_MODEL_LAYER,
+            ADD_MASK_LAYER,
+            REMOVE_MASK_LAYER,
+            UNSPECIFIED
+        };
+
+        MapModelChange( ActionType action, Revision mapModeRev, Layer* layer, int firstIndex =-1, int secondIndex =-1 ) 
+            : _action(action), _layer(layer), _modelRevision(mapModeRev), _firstIndex(firstIndex), _secondIndex(secondIndex) { }
+
+        const ActionType& getAction() const { return _action; }
+        const Revision& getRevision() const { return _modelRevision; }
+        int getFirstIndex() const { return _firstIndex; }
+        int getSecondIndex() const { return _secondIndex; }
+        Layer* getLayer() const { return _layer.get(); }
+        ImageLayer* getImageLayer() const { return dynamic_cast<ImageLayer*>(_layer.get()); }
+        ElevationLayer* getElevationLayer() const { return dynamic_cast<ElevationLayer*>(_layer.get()); }
+        ModelLayer* getModelLayer() const { return dynamic_cast<ModelLayer*>(_layer.get()); }
+        MaskLayer* getMaskLayer() const { return dynamic_cast<MaskLayer*>(_layer.get()); }
+
+    private:
+        ActionType _action;
+        osg::ref_ptr<Layer> _layer;
+        Revision _modelRevision;
+        int _firstIndex, _secondIndex;
+    };
+}
+
+#endif // OSGEARTH_MAP_MODEL_CHANGE_H
diff --git a/src/osgEarth/MapNode.cpp b/src/osgEarth/MapNode.cpp
index 3fbe086..ff75556 100644
--- a/src/osgEarth/MapNode.cpp
+++ b/src/osgEarth/MapNode.cpp
@@ -22,7 +22,7 @@
 #include <osgEarth/MaskNode>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/OverlayDecorator>
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarth/TextureCompositor>
@@ -315,6 +315,8 @@ MapNode::init()
         _overlayDecorator->setTextureSize( *_mapNodeOptions.overlayTextureSize() );
     if ( _mapNodeOptions.overlayMipMapping().isSet() )
         _overlayDecorator->setMipMapping( *_mapNodeOptions.overlayMipMapping() );
+    if ( _mapNodeOptions.overlayAttachStencil().isSet() )
+        _overlayDecorator->setAttachStencil( *_mapNodeOptions.overlayAttachStencil() );
 
     addTerrainDecorator( _overlayDecorator );
 
@@ -345,9 +347,14 @@ MapNode::init()
     // Install top-level shader programs:
     if ( Registry::capabilities().supportsGLSL() )
     {
+        // Note. We use OVERRIDE here so that child object that use the ShaderGenerator
+        // don't have to worry about installing default shaders. The usage pattern is
+        // to use PROTECTED mode if you want to override the defaults in (say) a ModelLayer
+        // or in a TextureCompositor.
+
         VirtualProgram* vp = new VirtualProgram();
         vp->setName( "MapNode" );
-        vp->installDefaultColoringAndLightingShaders();
+        vp->installDefaultColoringAndLightingShaders( 0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
         ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
     }
 
@@ -367,7 +374,7 @@ MapNode::~MapNode()
     //Remove our model callback from any of the model layers in the map    
     for (osgEarth::ModelLayerVector::iterator itr = modelLayers.begin(); itr != modelLayers.end(); ++itr)
     {
-        this->onModelLayerRemoved( itr->get() );        
+        this->onModelLayerRemoved( itr->get() );
     }
 }
 
diff --git a/src/osgEarth/MapNodeObserver b/src/osgEarth/MapNodeObserver
index 10faab3..2e71ff0 100644
--- a/src/osgEarth/MapNodeObserver
+++ b/src/osgEarth/MapNodeObserver
@@ -47,6 +47,11 @@ namespace osgEarth
          * Gets the map node from its observer reference
          */
         virtual MapNode* getMapNode() =0;
+
+        /**
+         * dtor
+         */
+        virtual ~MapNodeObserver() { }
     };
 
 
diff --git a/src/osgEarth/MapNodeOptions b/src/osgEarth/MapNodeOptions
index 1c0d71d..5915971 100644
--- a/src/osgEarth/MapNodeOptions
+++ b/src/osgEarth/MapNodeOptions
@@ -77,6 +77,12 @@ namespace osgEarth
         const optional<bool>& overlayMipMapping() const { return _overlayMipMapping; }
 
         /**
+         * Whether to attach the RTT camera to the stencil buffer
+         */
+        optional<bool>& overlayAttachStencil() { return _overlayAttachStencil; }
+        const optional<bool>& overlayAttachStencil() const { return _overlayAttachStencil; }
+
+        /**
          * Options to conigure the terrain engine (the component that renders the
          * terrain surface).
          */
@@ -98,6 +104,7 @@ namespace osgEarth
         optional<bool>     _overlayBlending;
         optional<unsigned> _overlayTextureSize;
         optional<bool>     _overlayMipMapping;
+        optional<bool>     _overlayAttachStencil;
 
         optional<Config> _terrainOptionsConf;
         TerrainOptions* _terrainOptions;
diff --git a/src/osgEarth/MapNodeOptions.cpp b/src/osgEarth/MapNodeOptions.cpp
index 6374747..00e48e3 100644
--- a/src/osgEarth/MapNodeOptions.cpp
+++ b/src/osgEarth/MapNodeOptions.cpp
@@ -39,7 +39,8 @@ _overlayVertexWarping( false ),
 _overlayBlending     ( true ),
 _overlayMipMapping   ( false ),
 _overlayTextureSize  ( 4096 ),
-_terrainOptions      ( 0L )
+_terrainOptions      ( 0L ),
+_overlayAttachStencil( true )
 {
     mergeConfig( conf );
 }
@@ -52,6 +53,7 @@ _overlayVertexWarping( false ),
 _overlayBlending     ( true ),
 _overlayTextureSize  ( 4096 ),
 _overlayMipMapping   ( false ),
+_overlayAttachStencil( true ),
 _terrainOptions      ( 0L )
 {
     setTerrainOptions( to );
@@ -65,7 +67,8 @@ _overlayVertexWarping( false ),
 _overlayBlending     ( true ),
 _overlayTextureSize  ( 4096 ),
 _overlayMipMapping   ( false ),
-_terrainOptions      ( 0L )
+_terrainOptions      ( 0L ),
+_overlayAttachStencil( true )
 {
     mergeConfig( rhs.getConfig() );
 }
@@ -86,14 +89,15 @@ MapNodeOptions::getConfig() const
     Config conf; // start with a fresh one since this is a FINAL object  // = ConfigOptions::getConfig();
     conf.key() = "options";
 
-    conf.updateObjIfSet( "proxy",                _proxySettings );
-    conf.updateIfSet   ( "cache_only",           _cacheOnly );
-    conf.updateIfSet   ( "lighting",             _enableLighting );
-    conf.updateIfSet   ( "terrain",              _terrainOptionsConf );
-    conf.updateIfSet   ( "overlay_warping",      _overlayVertexWarping );
-    conf.updateIfSet   ( "overlay_blending",     _overlayBlending );
-    conf.updateIfSet   ( "overlay_texture_size", _overlayTextureSize );
-    conf.updateIfSet   ( "overlay_mipmapping",   _overlayMipMapping );
+    conf.updateObjIfSet( "proxy",                  _proxySettings );
+    conf.updateIfSet   ( "cache_only",             _cacheOnly );
+    conf.updateIfSet   ( "lighting",               _enableLighting );
+    conf.updateIfSet   ( "terrain",                _terrainOptionsConf );
+    conf.updateIfSet   ( "overlay_warping",        _overlayVertexWarping );
+    conf.updateIfSet   ( "overlay_blending",       _overlayBlending );
+    conf.updateIfSet   ( "overlay_texture_size",   _overlayTextureSize );
+    conf.updateIfSet   ( "overlay_mipmapping",     _overlayMipMapping );
+    conf.updateIfSet   ( "overlay_attach_stencil", _overlayAttachStencil );
 
     return conf;
 }
@@ -103,13 +107,14 @@ MapNodeOptions::mergeConfig( const Config& conf )
 {
     ConfigOptions::mergeConfig( conf );
 
-    conf.getObjIfSet( "proxy",                _proxySettings );
-    conf.getIfSet   ( "cache_only",           _cacheOnly );
-    conf.getIfSet   ( "lighting",             _enableLighting );
-    conf.getIfSet   ( "overlay_warping",      _overlayVertexWarping );
-    conf.getIfSet   ( "overlay_blending",     _overlayBlending );
-    conf.getIfSet   ( "overlay_texture_size", _overlayTextureSize );
-    conf.getIfSet   ( "overlay_mipmapping",   _overlayMipMapping );
+    conf.getObjIfSet( "proxy",                  _proxySettings );
+    conf.getIfSet   ( "cache_only",             _cacheOnly );
+    conf.getIfSet   ( "lighting",               _enableLighting );
+    conf.getIfSet   ( "overlay_warping",        _overlayVertexWarping );
+    conf.getIfSet   ( "overlay_blending",       _overlayBlending );
+    conf.getIfSet   ( "overlay_texture_size",   _overlayTextureSize );
+    conf.getIfSet   ( "overlay_mipmapping",     _overlayMipMapping );
+    conf.getIfSet   ( "overlay_attach_stencil", _overlayAttachStencil );
 
     if ( conf.hasChild( "terrain" ) )
     {
diff --git a/src/osgEarth/ModelLayer b/src/osgEarth/ModelLayer
index ab55805..be797d1 100644
--- a/src/osgEarth/ModelLayer
+++ b/src/osgEarth/ModelLayer
@@ -82,14 +82,6 @@ namespace osgEarth
         optional<bool>& overlay() { return _overlay; }
         const optional<bool>& overlay() const { return _overlay; }
 
-        /**
-         * TEMPORARY option that will turn off all shader program composition
-         * on the model layer. Need this temporarily to support external model
-         * references until we get a proper shader gen working
-         */
-        optional<bool>& disableShaders() { return _disableShaderComp; }
-        const optional<bool>& disableShaders() const { return _disableShaderComp; }
-
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
@@ -98,13 +90,12 @@ namespace osgEarth
         void fromConfig( const Config& conf );
         void setDefaults();
 
-        optional<std::string> _name;
-        optional<bool> _overlay;
+        optional<std::string>        _name;
+        optional<bool>               _overlay;
         optional<ModelSourceOptions> _driver;
-        optional<bool> _enabled;
-        optional<bool> _visible;
-        optional<bool> _lighting;
-        optional<bool> _disableShaderComp;
+        optional<bool>               _enabled;
+        optional<bool>               _visible;
+        optional<bool>               _lighting;
     };
 
     /**
@@ -209,8 +200,6 @@ namespace osgEarth
         osg::ref_ptr<ModelSource>    _modelSource;
         const ModelLayerOptions      _initOptions;
         ModelLayerOptions            _runtimeOptions;
-        //osg::ref_ptr< osg::Node >    _node;
-        //osg::ref_ptr<osgDB::Options> _dbOptions;
         Revision                     _modelSourceRev;
         ModelLayerCallbackList       _callbacks;
         UpdateLightingUniformsHelper _updateLightingUniformsHelper;
diff --git a/src/osgEarth/ModelLayer.cpp b/src/osgEarth/ModelLayer.cpp
index 8f90eca..fc798e7 100644
--- a/src/osgEarth/ModelLayer.cpp
+++ b/src/osgEarth/ModelLayer.cpp
@@ -20,7 +20,6 @@
 #include <osgEarth/Map>
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
-#include <osgEarth/ShaderComposition>
 #include <osg/Depth>
 
 #define LC "[ModelLayer] "
@@ -67,11 +66,10 @@ ConfigOptions()
 void
 ModelLayerOptions::setDefaults()
 {
-    _overlay.init( false );
-    _enabled.init( true );
-    _visible.init( true );
-    _lighting.init( true );
-    _disableShaderComp.init( false );
+    _overlay.init     ( false );
+    _enabled.init     ( true );
+    _visible.init     ( true );
+    _lighting.init    ( true );
 }
 
 Config
@@ -86,9 +84,6 @@ ModelLayerOptions::getConfig() const
     conf.updateIfSet( "visible", _visible );
     conf.updateIfSet( "lighting", _lighting );
 
-    // temporary.
-    conf.updateIfSet( "disable_shaders", _disableShaderComp );
-
     // Merge the ModelSource options
     if ( driver().isSet() )
         conf.merge( driver()->getConfig() );
@@ -105,9 +100,6 @@ ModelLayerOptions::fromConfig( const Config& conf )
     conf.getIfSet( "visible", _visible );
     conf.getIfSet( "lighting", _lighting );
 
-    // temporary.
-    conf.getIfSet( "disable_shaders", _disableShaderComp );
-
     if ( conf.hasValue("driver") )
         driver() = ModelSourceOptions(conf);
 }
@@ -181,12 +173,6 @@ ModelLayer::createSceneGraph(const Map*            map,
 
     if ( _modelSource.valid() )
     {
-        //// if the model source has changed, regenerate the node.
-        //if ( _node.valid() && !_modelSource->inSyncWith(_modelSourceRev) )
-        //{
-        //    _node = 0L;
-        //}
-
         node = _modelSource->createNode( map, dbOptions, progress );
 
         if ( node )
@@ -201,19 +187,6 @@ ModelLayer::createSceneGraph(const Map*            map,
                 setLightingEnabled( *_runtimeOptions.lightingEnabled() );
             }
 
-            if ( Registry::instance()->getCapabilities().supportsGLSL() )
-            {
-                node->addCullCallback( new UpdateLightingUniformsHelper() );
-
-                if ( _runtimeOptions.disableShaders() == true )
-                {
-                    osg::StateSet* ss = node->getOrCreateStateSet();
-                    ss->setAttributeAndModes(
-                        new osg::Program(), 
-                        osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
-                }
-            }
-
             if ( _modelSource->getOptions().depthTestEnabled() == false )
             {
                 osg::StateSet* ss = node->getOrCreateStateSet();
@@ -221,6 +194,12 @@ ModelLayer::createSceneGraph(const Map*            map,
                 ss->setRenderBinDetails( 99999, "RenderBin" ); //TODO: configure this bin ...
             }
 
+            if ( Registry::capabilities().supportsGLSL() )
+            {
+                // install a callback that keeps the shader uniforms up to date
+                node->addCullCallback( new UpdateLightingUniformsHelper() );
+            }
+
             _modelSource->sync( _modelSourceRev );
 
             // save an observer reference to the node so we can change the visibility/lighting/etc.
@@ -279,14 +258,6 @@ ModelLayer::setLightingEnabled( bool value )
                 (osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED) );
         }
     }
-
-    //if ( _node.valid() )
-    //{
-    //    _node->getOrCreateStateSet()->setMode( 
-    //        GL_LIGHTING, value ? osg::StateAttribute::ON : 
-    //        (osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED) );
-
-    //}
 }
 
 bool
diff --git a/src/osgEarth/OverlayDecorator b/src/osgEarth/OverlayDecorator
index cd0f064..c0b7023 100644
--- a/src/osgEarth/OverlayDecorator
+++ b/src/osgEarth/OverlayDecorator
@@ -103,6 +103,14 @@ namespace osgEarth
         void setOverlayBlending( bool value );
         bool getOverlayBlending() const { return _rttBlending; }
 
+
+        /**
+         * Whether to attach the RTT to camera to the stencil buffer.  Default = true.
+         * Some older cards don't have very good support 
+         */
+        void setAttachStencil( bool value );
+        bool getAttachStencil() const;
+
         void setOverlayGraphTraversalMask( unsigned mask );
 
     public: // TerrainDecorator
@@ -127,6 +135,7 @@ namespace osgEarth
         unsigned                      _rttTraversalMask;
         
         double                        _maxHorizonDistance;
+        bool                          _attachStencil;
 
         osg::ref_ptr<const SpatialReference>    _srs;
         osg::ref_ptr<const osg::EllipsoidModel> _ellipsoid;
@@ -136,14 +145,14 @@ namespace osgEarth
         {
             osg::ref_ptr<osg::Camera>     _rttCamera;
             osg::ref_ptr<osg::Uniform>    _texGenUniform;
-            osg::ref_ptr<osg::TexGenNode> _texGenNode;
+            osg::ref_ptr<osg::TexGen>     _texGen;
             osg::Matrixd                  _rttViewMatrix;
             osg::Matrixd                  _rttProjMatrix;
             osg::ref_ptr<osg::StateSet>   _subgraphStateSet;
         };
 
-        //typedef std::map<osg::Camera*, PerViewData> PerViewDataMap;
-        typedef std::map<osg::NodeVisitor*, PerViewData> PerViewDataMap;
+        typedef std::map<osg::Camera*, PerViewData> PerViewDataMap;
+        //typedef std::map<osg::NodeVisitor*, PerViewData> PerViewDataMap;
         PerViewDataMap _perViewData;
         Threading::ReadWriteMutex _perViewDataMutex;
 
@@ -155,7 +164,8 @@ namespace osgEarth
         void initializePerViewData( PerViewData& );
         void initSubgraphShaders( PerViewData& );
         bool checkNeedsUpdate( PerViewData& );
-        PerViewData& getPerViewData( osg::NodeVisitor* key );
+        PerViewData& getPerViewData( osg::Camera* key );
+        //PerViewData& getPerViewData( osg::NodeVisitor* key );
 
     public:
         // marker class for DrapeableNode support.
diff --git a/src/osgEarth/OverlayDecorator.cpp b/src/osgEarth/OverlayDecorator.cpp
index b95f06e..1c4759f 100644
--- a/src/osgEarth/OverlayDecorator.cpp
+++ b/src/osgEarth/OverlayDecorator.cpp
@@ -17,10 +17,11 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include <osgEarth/OverlayDecorator>
+#include <osgEarth/MapInfo>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/TextureCompositor>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/Capabilities>
 #include <osg/Texture2D>
 #include <osg/TexEnv>
@@ -45,166 +46,6 @@ using namespace osgEarth;
 
 namespace
 {
-#if 0
-    /**
-     * Creates a polytope that minimally bounds a bounding sphere
-     * in world space.
-     */
-    void computeWorldBoundingPolytope(const osg::BoundingSphere&  bs, 
-                                      const SpatialReference*     srs,
-                                      bool                        geocentric,
-                                      osg::Polytope&              out_polytope)
-    {
-        out_polytope.clear();
-        const osg::EllipsoidModel* ellipsoid = srs->getEllipsoid();
-
-        // add planes for the four sides of the BS. Normals point inwards.
-        out_polytope.add( osg::Plane(osg::Vec3d( 1, 0,0), osg::Vec3d(-bs.radius(),0,0)) );
-        out_polytope.add( osg::Plane(osg::Vec3d(-1, 0,0), osg::Vec3d( bs.radius(),0,0)) );
-        out_polytope.add( osg::Plane(osg::Vec3d( 0, 1,0), osg::Vec3d(0, -bs.radius(),0)) );
-        out_polytope.add( osg::Plane(osg::Vec3d( 0,-1,0), osg::Vec3d(0,  bs.radius(),0)) );
-
-        // for a projected map, we're done. For a geocentric one, add a bottom cap.
-        if ( geocentric )
-        {
-            // add a bottom cap, unless the bounds are sufficiently large.
-            double minRad = std::min(ellipsoid->getRadiusPolar(), ellipsoid->getRadiusEquator());
-            double maxRad = std::max(ellipsoid->getRadiusPolar(), ellipsoid->getRadiusEquator());
-            double zeroOffset = bs.center().length();
-            if ( zeroOffset > minRad * 0.1 )
-            {
-                out_polytope.add( osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,-maxRad+zeroOffset)) );
-            }
-        }
-
-        // transform the clipping planes ito world space localized about the center point
-        GeoPoint refPoint;
-        refPoint.fromWorld( srs, bs.center() );
-        osg::Matrix local2world;
-        refPoint.createLocalToWorld( local2world );
-
-        out_polytope.transform( local2world );
-    }
-
-
-    /**
-     * Tests whether a "cohesive" point set intersects a polytope. This differs from
-     * Polytope::contains(verts); that function tests each point individually, whereas
-     * this method tests the point set as a whole (i.e. as the border points of a solid --
-     * we are testing whether this solid intersects the polytope.)
-     */
-    bool pointSetIntersectsClippingPolytope(const std::vector<osg::Vec3>& points, osg::Polytope& pt)
-    {
-        osg::Polytope::PlaneList& planes = pt.getPlaneList();
-        for( osg::Polytope::PlaneList::iterator plane = planes.begin(); plane != planes.end(); ++plane )
-        {
-            unsigned outsides = 0;
-            for( std::vector<osg::Vec3>::const_iterator point = points.begin(); point != points.end(); ++point )
-            {
-                bool outside = plane->distance( *point ) < 0.0f;
-                if ( outside ) outsides++;
-                else break;
-            }
-            if ( outsides == points.size() ) 
-                return false;
-        }
-        return true;
-    }
-
-
-    /**
-     * Visitor that computes a bounding sphere for the geometry that intersects
-     * a frustum polyhedron. Since this is used to project geometry on to the 
-     * terrain surface, it has to account for geometry that is not clamped --
-     * so instead of using the normal bounding sphere it computes a world-space
-     * polytope for each geometry and interests that with the frustum.
-     */
-    struct ComputeBoundsWithinFrustum : public OverlayDecorator::InternalNodeVisitor
-    {
-        std::vector<osg::Vec3>  _frustumVerts;
-
-        ComputeBoundsWithinFrustum(const osgShadow::ConvexPolyhedron& frustumPH, 
-                                   const SpatialReference* srs,
-                                   bool                    geocentric,
-                                   osg::BoundingSphere&    out_bs)
-            : InternalNodeVisitor(),
-              _srs       ( srs ),
-              _geocentric( geocentric ),
-              _bs        ( out_bs )
-        {
-            frustumPH.getPolytope( _originalPT );
-
-            _polytopeStack.push( _originalPT );
-            _local2worldStack.push( osg::Matrix::identity() );
-            _world2localStack.push( osg::Matrix::identity() );
-
-            // extract the corner verts from the frustum polyhedron; we will use those to
-            // test for intersection.
-            std::vector<osg::Vec3d> temp;
-            temp.reserve( 8 );
-            frustumPH.getPoints( temp );
-            for( unsigned i=0; i<temp.size(); ++i )
-                _frustumVerts.push_back(temp[i]);
-        }
-
-        bool contains( const osg::BoundingSphere& bs )
-        {
-            osg::BoundingSphere worldBS( bs.center() * _local2worldStack.top(), bs.radius() );
-            osg::Polytope bsWorldPT;
-            computeWorldBoundingPolytope( worldBS, _srs, _geocentric, bsWorldPT );
-            return pointSetIntersectsClippingPolytope( _frustumVerts, bsWorldPT );
-        }
-
-        void apply( osg::Node& node )
-        {
-            const osg::BoundingSphere& bs = node.getBound();
-            if ( contains(bs) )
-            {
-                traverse( node );
-            }
-        }
-
-        void apply( osg::Geode& node )
-        {
-            const osg::BoundingSphere& bs = node.getBound();
-            if ( contains(bs) )
-            {
-                _bs.expandBy( osg::BoundingSphere(
-                    bs.center() * _local2worldStack.top(),
-                    bs.radius() ) );
-            }
-        }
-
-        void apply( osg::Transform& transform )
-        {
-            osg::Matrixd local2world;
-            transform.computeLocalToWorldMatrix( local2world, this );
-
-            _local2worldStack.push( local2world );
-
-            _polytopeStack.push( _originalPT );
-            _polytopeStack.top().transformProvidingInverse( local2world );
-
-            osg::Matrix world2local;
-            world2local.invert( local2world );
-            _world2localStack.push( world2local );
-
-            traverse(transform);
-
-            _local2worldStack.pop();
-            _polytopeStack.pop();
-        }
-
-        osg::BoundingSphere&      _bs;
-        const SpatialReference*   _srs;
-        bool                      _geocentric;
-        osg::Polytope             _originalPT;
-        std::stack<osg::Polytope> _polytopeStack;
-        std::stack<osg::Matrixd>  _local2worldStack, _world2localStack;
-    };
-#endif
-
-
     /**
      * This method takes a set of verts and finds the nearest and farthest distances from
      * the points to the camera. It does this calculation in the plane defined by the
@@ -275,7 +116,8 @@ _rttBlending     ( true ),
 _updatePending   ( false ),
 _dumpRequested   ( false ),
 _rttTraversalMask( ~0 ),
-_maxHorizonDistance( DBL_MAX )
+_maxHorizonDistance( DBL_MAX ),
+_attachStencil   ( true )
 {
     // nop
 }
@@ -323,14 +165,14 @@ OverlayDecorator::initializePerViewData( PerViewData& pvd )
     // create the projected texture:
     osg::Texture2D* projTexture = new osg::Texture2D();
     projTexture->setTextureSize( *_textureSize, *_textureSize );
-    projTexture->setInternalFormat( GL_RGBA8 );
+    projTexture->setInternalFormat( GL_RGBA );
     projTexture->setSourceFormat( GL_RGBA );
     projTexture->setSourceType( GL_UNSIGNED_BYTE );
     projTexture->setFilter( osg::Texture::MIN_FILTER, _mipmapping? osg::Texture::LINEAR_MIPMAP_LINEAR: osg::Texture::LINEAR );
     projTexture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
-    projTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER );
-    projTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER );
-    projTexture->setWrap( osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_BORDER );
+    projTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); //CLAMP_TO_BORDER );
+    projTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
+    projTexture->setWrap( osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE );
     projTexture->setBorderColor( osg::Vec4(0,0,0,0) );
 
     // set up the RTT camera:
@@ -348,30 +190,40 @@ OverlayDecorator::initializePerViewData( PerViewData& pvd )
 
     pvd._rttCamera->attach( osg::Camera::COLOR_BUFFER, projTexture, 0, 0, _mipmapping );
 
-    // try a depth-packed buffer. failing that, try a normal one.. if the FBO doesn't support
-    // that (which is doesn't on some GPUs like Intel), it will automatically fall back on 
-    // a PBUFFER_RTT impl
-    if ( Registry::instance()->getCapabilities().supportsDepthPackedStencilBuffer() )
-        pvd._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH_STENCIL_EXT );
-    else
-        pvd._rttCamera->attach( osg::Camera::STENCIL_BUFFER, GL_STENCIL_INDEX );
+    if (_attachStencil) {
+        // try a depth-packed buffer. failing that, try a normal one.. if the FBO doesn't support
+        // that (which is doesn't on some GPUs like Intel), it will automatically fall back on 
+        // a PBUFFER_RTT impl
+        if ( Registry::instance()->getCapabilities().supportsDepthPackedStencilBuffer() )
+        {
+#ifdef OSG_GLES2_AVAILABLE 
+            pvd._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH24_STENCIL8_EXT );
+#else
+            pvd._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH_STENCIL_EXT );
+#endif
+        }
+        else
+        {
+            pvd._rttCamera->attach( osg::Camera::STENCIL_BUFFER, GL_STENCIL_INDEX );
+        }
+    }
 
     osg::StateSet* rttStateSet = pvd._rttCamera->getOrCreateStateSet();
 
     rttStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
 
     // install a new default shader program that replaces anything from above.
-    VirtualProgram* vp = new VirtualProgram();
-    vp->setName( "overlay rtt" );
-    vp->installDefaultColoringAndLightingShaders();
-    vp->setInheritShaders( false );
-    rttStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+    if ( _useShaders )
+    {
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setName( "overlay rtt" );
+        vp->installDefaultColoringAndLightingShaders();
+        vp->setInheritShaders( false );
+        rttStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+    }
     
     if ( _rttBlending )
     {
-        //osg::BlendFunc* blendFunc = new osg::BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        //osg::BlendFunc* blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
         //Setup a separate blend function for the alpha components and the RGB components.  
         //Because the destination alpha is initialized to 0 instead of 1
         osg::BlendFunc* blendFunc = 0;        
@@ -410,23 +262,23 @@ OverlayDecorator::initializePerViewData( PerViewData& pvd )
     // assemble the subgraph stateset:
     pvd._subgraphStateSet = new osg::StateSet();
 
-    // set up the subgraph to receive the projected texture:
-    pvd._subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON );
-    pvd._subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON );
-    pvd._subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON );
-    pvd._subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON );
     pvd._subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, projTexture, osg::StateAttribute::ON );
     
     if ( _useShaders )
     {            
-        // set up the shaders
+        // GPU path
         initSubgraphShaders( pvd );
     }
     else
     {
-        // FFP path:
-        pvd._texGenNode = new osg::TexGenNode();
-        pvd._texGenNode->setTexGen( new osg::TexGen() );
+        // FFP path
+        pvd._texGen = new osg::TexGen();
+        pvd._texGen->setMode( osg::TexGen::EYE_LINEAR );
+        pvd._subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, pvd._texGen.get(), 1 );
+
+        osg::TexEnv* env = new osg::TexEnv();
+        env->setMode( osg::TexEnv::DECAL );
+        pvd._subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, env, 1 );
     }
 }
 
@@ -440,10 +292,10 @@ OverlayDecorator::initSubgraphShaders( PerViewData& pvd )
     set->setAttributeAndModes( vp, osg::StateAttribute::ON );
 
     // sampler for projected texture:
-    set->getOrCreateUniform( "osgearth_overlay_ProjTex", osg::Uniform::SAMPLER_2D )->set( *_textureUnit );
+    set->getOrCreateUniform( "oe_overlay_ProjTex", osg::Uniform::SAMPLER_2D )->set( *_textureUnit );
 
     // the texture projection matrix uniform.
-    pvd._texGenUniform = set->getOrCreateUniform( "osgearth_overlay_TexGenMatrix", osg::Uniform::FLOAT_MAT4 );
+    pvd._texGenUniform = set->getOrCreateUniform( "oe_overlay_TexGenMatrix", osg::Uniform::FLOAT_MAT4 );
 
     // vertex shader - subgraph
     std::string vertexSource = Stringify()
@@ -451,16 +303,16 @@ OverlayDecorator::initSubgraphShaders( PerViewData& pvd )
 #ifdef OSG_GLES2_AVAILABLE
         << "precision mediump float;\n"
 #endif
-        << "uniform mat4 osgearth_overlay_TexGenMatrix; \n"
+        << "uniform mat4 oe_overlay_TexGenMatrix; \n"
         << "uniform mat4 osg_ViewMatrixInverse; \n"
         << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n"
 
-        << "void osgearth_overlay_vertex(void) \n"
+        << "void oe_overlay_vertex(void) \n"
         << "{ \n"
-        << "    osg_TexCoord["<< *_textureUnit << "] = osgearth_overlay_TexGenMatrix * osg_ViewMatrixInverse * gl_ModelViewMatrix * gl_Vertex; \n"
+        << "    osg_TexCoord["<< *_textureUnit << "] = oe_overlay_TexGenMatrix * osg_ViewMatrixInverse * gl_ModelViewMatrix * gl_Vertex; \n"
         << "} \n";
 
-    vp->setFunction( "osgearth_overlay_vertex", vertexSource, ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
+    vp->setFunction( "oe_overlay_vertex", vertexSource, ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
 
     // fragment shader - subgraph
     std::string fragmentSource = Stringify()
@@ -468,16 +320,16 @@ OverlayDecorator::initSubgraphShaders( PerViewData& pvd )
 #ifdef OSG_GLES2_AVAILABLE
         << "precision mediump float;\n"
 #endif
-        << "uniform sampler2D osgearth_overlay_ProjTex; \n"
+        << "uniform sampler2D oe_overlay_ProjTex; \n"
         << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n"
-        << "void osgearth_overlay_fragment( inout vec4 color ) \n"
+        << "void oe_overlay_fragment( inout vec4 color ) \n"
         << "{ \n"
         << "    vec2 texCoord = osg_TexCoord["<< *_textureUnit << "].xy / osg_TexCoord["<< *_textureUnit << "].q; \n"
-        << "    vec4 texel = texture2D(osgearth_overlay_ProjTex, texCoord); \n"  
+        << "    vec4 texel = texture2D(oe_overlay_ProjTex, texCoord); \n"  
         << "    color = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a); \n"
         << "} \n";
 
-    vp->setFunction( "osgearth_overlay_fragment", fragmentSource, ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING );
+    vp->setFunction( "oe_overlay_fragment", fragmentSource, ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING );
 }
 
 void
@@ -575,6 +427,18 @@ OverlayDecorator::setOverlayBlending( bool value )
     }
 }
 
+bool
+OverlayDecorator::getAttachStencil() const
+{
+    return _attachStencil;
+}
+
+void
+OverlayDecorator::setAttachStencil( bool value )
+{
+    _attachStencil = value;
+}
+
 void
 OverlayDecorator::onInstall( TerrainEngineNode* engine )
 {
@@ -596,11 +460,13 @@ OverlayDecorator::onInstall( TerrainEngineNode* engine )
     // see whether we want shader support:
     // TODO: this is not stricty correct; you might still want to use shader overlays
     // in multipass mode, AND you might want FFP overlays in multitexture-FFP mode.
-    _useShaders = engine->getTextureCompositor()->usesShaderComposition();
+    _useShaders = 
+        Registry::capabilities().supportsGLSL() && 
+        engine->getTextureCompositor()->usesShaderComposition();
 
     if ( !_textureSize.isSet() )
     {
-        unsigned maxSize = Registry::instance()->getCapabilities().getMaxFastTextureSize();
+        unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
         _textureSize.init( osg::minimum( 4096u, maxSize ) );
 
         OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
@@ -639,7 +505,7 @@ OverlayDecorator::updateRTTCamera( OverlayDecorator::PerViewData& pvd )
     }
     else
     {
-        pvd._texGenNode->getTexGen()->setPlanesFromMatrix( MVPT );
+        pvd._texGen->setPlanesFromMatrix( MVPT );
     }
 }
 
@@ -933,7 +799,8 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
 }
 
 OverlayDecorator::PerViewData&
-OverlayDecorator::getPerViewData(osg::NodeVisitor* key)
+OverlayDecorator::getPerViewData(osg::Camera* key)
+//OverlayDecorator::getPerViewData(osg::NodeVisitor* key)
 {
     // first check for it:
     {
@@ -967,54 +834,47 @@ OverlayDecorator::getPerViewData(osg::NodeVisitor* key)
 void
 OverlayDecorator::traverse( osg::NodeVisitor& nv )
 {
-    bool isCull = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR;
-
     if ( _overlayGraph.valid() && _textureUnit.isSet() )
     {
         // in the CULL traversal, find the per-view data associated with the 
         // cull visitor's current camera view and work with that:
-        if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
+        if ( nv.getVisitorType() == nv.CULL_VISITOR )
         {
             osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( &nv );
-            PerViewData& pvd = getPerViewData( cv );
-            if ( cv->getCurrentCamera() )
+            osg::Camera* camera = cv->getCurrentCamera();
+            if ( camera != 0L && (_rttTraversalMask & nv.getTraversalMask()) != 0 )
             {
-                if ( (_rttTraversalMask & nv.getTraversalMask()) != 0 )
-                {
-                    if (checkNeedsUpdate(pvd))
-                    {
-                        updateRTTCamera(pvd);
-                    }
+                PerViewData& pvd = getPerViewData( camera );
 
-                    if ( pvd._texGenNode.valid() ) // FFP only
-                        pvd._texGenNode->accept( nv );
-
-                    cull( cv, pvd );
-                    pvd._rttCamera->accept( nv );
+                if (checkNeedsUpdate(pvd))
+                {
+                    updateRTTCamera(pvd);
                 }
-                else
+
+                if ( pvd._texGen.valid() )
                 {
-                    osg::Group::traverse(nv);
+                    // FFP path only
+                    cv->getCurrentRenderBin()->getStage()->addPositionedTextureAttribute(
+                        *_textureUnit, cv->getModelViewMatrix(), pvd._texGen.get() );
                 }
-            }
 
-            // debug-- (draws the overlay at its native location as well)
-            //_overlayGraph->accept(nv);
-        }
+                cull( cv, pvd );
 
-        else if ( nv.getVisitorType() == nv.UPDATE_VISITOR )
-        {
-            if ( _overlayGraph.valid() )
+                pvd._rttCamera->accept( nv );
+            }
+            else
             {
-                _overlayGraph->accept( nv );
+                osg::Group::traverse(nv);
             }
-            osg::Group::traverse( nv );
+
+            // debug-- (draws the overlay at its native location as well)
+            //_overlayGraph->accept(nv);
         }
 
         else
         {
-            // Some other type of visitor (like an intersection). Skip the RTT camera and
-            // traverse the overlay graph directly.
+            // Some other type of visitor (like update or intersection). Skip the RTT camera
+            // and traverse the overlay graph directly.
             if ( _overlayGraph.valid() )
             {
                 _overlayGraph->accept( nv );
diff --git a/src/osgEarth/Progress b/src/osgEarth/Progress
index 2c3bc68..767be74 100644
--- a/src/osgEarth/Progress
+++ b/src/osgEarth/Progress
@@ -39,23 +39,53 @@ namespace osgEarth
         /**
         * Callback function that will be called.
         * @param current
-        *        The current amount of work to be done.
+        *        The amount of work done in the current stage
         * @param total
-        *        The total amount of work to be done.
+        *        The total amount of work to be done in the current stage
+        * @param stage
+        *        Stage of the operation we're currently in
+        * @param totalStages
+        *        Total number of stages in the operation
         * @param msg
         *        Description of what is being done. Useful when total is unknown.
         * @param returns
         *        Returns true if the current task should be cancelled, false otherwise.
         */
-        virtual bool reportProgress(double current, double total, const std::string& msg = std::string());
+        virtual bool reportProgress(
+            double             current, 
+            double             total, 
+            unsigned           currentStage,
+            unsigned           totalStages,
+            const std::string& msg );
 
-        // called when the process starts
+        /**
+         * Convenience functions
+         */
+        bool reportProgress( double current, double total, const std::string& msg ) {
+            return reportProgress(current, total, 0, 1, msg );
+        }
+        bool reportProgress( double current, double total ) {
+            return reportProgress(current, total, 0, 1, "" );
+        }
+
+        /**
+         * called when the process starts
+         */
         virtual void onStarted() { }
 
-        // called when the process completed 
+        /**
+         * called when the process completed 
+         */
         virtual void onCompleted() { }
 
+        /**
+         * Sets the cancelation flag
+         */
         void cancel() { _canceled = true; }
+
+        /**
+         * Whether cancelation was requested
+         */
         bool isCanceled() const { return _canceled; }
 
         std::string& message() { return _message; }
@@ -63,18 +93,21 @@ namespace osgEarth
         /**
         *Whether or not the task should be retried.
         */
-        bool needsRetry() const { return _needsRetry;}
+        bool needsRetry() const { return _needsRetry; }
+
         /**
-        *Sets whether or not the task should be retried
-        */
+         * Sets whether or not the task should be retried
+         */
         void setNeedsRetry( bool needsRetry ) { _needsRetry = needsRetry; }
 
     protected:
-        volatile bool _canceled;
-        std::string _message;
-        volatile bool _needsRetry;
+        volatile unsigned _numStages;
+        volatile bool     _canceled;
+        std::string       _message;
+        volatile bool     _needsRetry;
     };
 
+
     /**
     * ConsoleProgressCallback is a simple ProgressCallback that reports progress to the console
     */
@@ -90,13 +123,24 @@ namespace osgEarth
         /**
         * Callback function that will be called.
         * @param current
-        *        The current amount of work to be done.
+        *        The amount of work done in the current stage
         * @param total
-        *        The total amount of work to be done.
+        *        The total amount of work to be done in the current stage
+        * @param stage
+        *        Stage of the operation we're currently in
+        * @param totalStages
+        *        Total number of stages in the operation
+        * @param msg
+        *        Description of what is being done. Useful when total is unknown.
         * @param returns
         *        Returns true if the current task should be cancelled, false otherwise.
         */
-        virtual bool reportProgress(double current, double total, const std::string& msg = std::string());
+        virtual bool reportProgress(
+            double             current, 
+            double             total, 
+            unsigned           currentStage,
+            unsigned           totalStages,
+            const std::string& msg );
     };
 }
 
diff --git a/src/osgEarth/Progress.cpp b/src/osgEarth/Progress.cpp
index 197ef62..5dcf059 100644
--- a/src/osgEarth/Progress.cpp
+++ b/src/osgEarth/Progress.cpp
@@ -24,13 +24,17 @@ using namespace osgEarth;
 
 ProgressCallback::ProgressCallback() :
 osg::Referenced( true ),
-_canceled(false),
-_needsRetry(false)
+_canceled      ( false ),
+_needsRetry    ( false )
 {
     //NOP
 }
 
-bool ProgressCallback::reportProgress(double /*current*/, double /*total*/, const std::string& /*msg*/) 
+bool ProgressCallback::reportProgress(double             current,
+                                      double             total,
+                                      unsigned           stage,
+                                      unsigned           numStages,
+                                      const std::string& msg )
 {
     return false;
 }
@@ -43,12 +47,17 @@ ProgressCallback()
 }
 
 bool
-ConsoleProgressCallback::reportProgress(double current, double total, const std::string& msg)
+ConsoleProgressCallback::reportProgress(double current, double total, 
+                                        unsigned stage, unsigned numStages,
+                                        const std::string& msg)
 {
     if (total > 0)
     {
         double percentComplete = (current / total) * 100.0;
-        OE_NOTICE << "Completed " << percentComplete << "% " << current << " of " << total << std::endl;
+        OE_NOTICE 
+            << "Stage " << (stage+1) << "/" << numStages 
+            << "; completed " << percentComplete << "% " << current << " of " << total 
+            << std::endl;
     }
     else
     {
diff --git a/src/osgEarth/Registry b/src/osgEarth/Registry
index ebc0a6d..d691ec1 100644
--- a/src/osgEarth/Registry
+++ b/src/osgEarth/Registry
@@ -43,6 +43,7 @@ namespace osgEarth
     class TaskServiceManager;
     class URIReadCallback;
     class ColorFilterRegistry;
+    class StateSetCache;
 
     /**
      * Application-wide global repository.
@@ -75,9 +76,20 @@ namespace osgEarth
         Cache* getCache() const;
         void setCache( Cache* cache );
 
-        /** The system-wide default cache policy */
+        /** The override cache policy (overrides all others if set) */
+        const optional<CachePolicy>& overrideCachePolicy() const { return _overrideCachePolicy; }
+        void setOverrideCachePolicy( const CachePolicy& policy );
+
+        /** The default cache policy (used when no policy is set) */
         const optional<CachePolicy>& defaultCachePolicy() const { return _defaultCachePolicy; }
-        void setDefaultCachePolicy( const CachePolicy& policy ) { _defaultCachePolicy = policy; }
+        void setDefaultCachePolicy( const CachePolicy& policy );
+
+        /**
+         * Gets the cache policy. 
+         * First checks for an override policy; then looks in the DB::Options for one,
+         * then checks for the default policy; then returns false as a last resort.
+         */
+        bool getCachePolicy( optional<CachePolicy>& cp, const osgDB::Options* options =0L ) const;
 
         /**
          * Whether the given filename is blacklisted
@@ -121,6 +133,14 @@ namespace osgEarth
         void setShaderFactory( ShaderFactory* lib );
 
         /**
+         * A default StateSetCache to use by any process that uses one.
+         * A StateSetCache assist in stateset sharing across multiple nodes.
+         */
+        StateSetCache* getStateSetCache() const;
+        void setStateSetCache( StateSetCache* cache );
+        static StateSetCache* stateSetCache() { return instance()->getStateSetCache(); }
+
+        /**
          * Gets a reference to the global task service manager.
          */
         TaskServiceManager* getTaskServiceManager() {
@@ -188,6 +208,7 @@ namespace osgEarth
 
         osg::ref_ptr<Cache> _cache;
         optional<CachePolicy> _defaultCachePolicy;
+        optional<CachePolicy> _overrideCachePolicy;
 
         typedef std::set<std::string> StringSet;
         StringSet _blacklistedFilenames;
@@ -211,6 +232,8 @@ namespace osgEarth
         typedef std::vector<const Units*> UnitsVector;
         UnitsVector _unitsVector;
 
+        osg::ref_ptr<StateSetCache> _stateSetCache;
+
         std::string _terrainEngineDriver;
     };
 }
diff --git a/src/osgEarth/Registry.cpp b/src/osgEarth/Registry.cpp
index ec39de7..dbaf68f 100644
--- a/src/osgEarth/Registry.cpp
+++ b/src/osgEarth/Registry.cpp
@@ -19,10 +19,12 @@
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
 #include <osgEarth/Cube>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/ShaderFactory>
 #include <osgEarth/TaskService>
 #include <osgEarth/IOTypes>
 #include <osgEarth/ColorFilter>
+#include <osgEarth/StateSetCache>
 #include <osgEarthDrivers/cache_filesystem/FileSystemCache>
 #include <osg/Notify>
 #include <osg/Version>
@@ -62,6 +64,7 @@ _terrainEngineDriver( "osgterrain" )
 
     _shaderLib = new ShaderFactory();
     _taskServiceManager = new TaskServiceManager();
+    _stateSetCache = new StateSetCache();
 
     // activate KMZ support
     osgDB::Registry::instance()->addArchiveExtension  ( "kmz" );    
@@ -108,23 +111,17 @@ _terrainEngineDriver( "osgterrain" )
     // activate cache-only mode from the environment
     if ( ::getenv("OSGEARTH_CACHE_ONLY") )
     {
-        _defaultCachePolicy = CachePolicy::CACHE_ONLY;
+        setOverrideCachePolicy( CachePolicy::CACHE_ONLY );
         OE_INFO << LC << "CACHE-ONLY MODE set from environment variable" << std::endl;
     }
 
     // activate no-cache mode from the environment
     else if ( ::getenv("OSGEARTH_NO_CACHE") )
     {
-        _defaultCachePolicy = CachePolicy::NO_CACHE;
+        setOverrideCachePolicy( CachePolicy::NO_CACHE );
         OE_INFO << LC << "NO-CACHE MODE set from environment variable" << std::endl;
     }
 
-    // if there's a default caching policy, add it to the default options.
-    if ( _defaultCachePolicy.isSet() )
-    {
-        _defaultCachePolicy->apply( _defaultOptions.get() );
-    }
-
     // set the default terrain engine driver from the environment
     const char* teStr = ::getenv("OSGEARTH_TERRAIN_ENGINE");
     if ( teStr )
@@ -282,6 +279,48 @@ Registry::getNamedProfile( const std::string& name ) const
         return NULL;
 }
 
+void
+Registry::setDefaultCachePolicy( const CachePolicy& value )
+{
+    _defaultCachePolicy = value;
+    if ( !_overrideCachePolicy.isSet() )
+        _defaultCachePolicy->apply(_defaultOptions.get());
+    else
+        _overrideCachePolicy->apply(_defaultOptions.get());
+}
+
+void
+Registry::setOverrideCachePolicy( const CachePolicy& value )
+{
+    _overrideCachePolicy = value;
+    _overrideCachePolicy->apply( _defaultOptions.get() );
+}
+
+bool
+Registry::getCachePolicy( optional<CachePolicy>& cp, const osgDB::Options* options ) const
+{
+    if ( overrideCachePolicy().isSet() )
+    {
+        // if there is a system-wide override in place, use it.
+        cp = overrideCachePolicy().value();
+    }
+    else 
+    {
+        // Try to read the cache policy from the db-options
+        CachePolicy::fromOptions( options, cp );
+
+        if ( !cp.isSet() )
+        {
+            if ( defaultCachePolicy().isSet() )
+            {
+                cp = defaultCachePolicy().value();
+            }
+        }
+    }
+
+    return cp.isSet();
+}
+
 osgEarth::Cache*
 Registry::getCache() const
 {
@@ -443,6 +482,18 @@ Registry::setDefaultTerrainEngineDriverName(const std::string& name)
     _terrainEngineDriver = name;
 }
 
+void
+Registry::setStateSetCache( StateSetCache* cache )
+{
+    _stateSetCache = cache;
+}
+
+StateSetCache*
+Registry::getStateSetCache() const
+{
+    return _stateSetCache.get();
+}
+
 
 //Simple class used to add a file extension alias for the earth_tile to the earth plugin
 class RegisterEarthTileExtension
diff --git a/src/osgEarth/Revisioning b/src/osgEarth/Revisioning
index 7f511c4..ac20587 100644
--- a/src/osgEarth/Revisioning
+++ b/src/osgEarth/Revisioning
@@ -157,8 +157,8 @@ namespace osgEarth
         struct DirtyCounter : public osg::Referenced
         {
             DirtyCounter(DirtyNotifier* owner) : _owner(owner), _count(1) { }
-            int            _count;
             DirtyNotifier* _owner;
+            int            _count;
             friend class DirtyNotifer;
         };
         osg::ref_ptr<DirtyCounter>                     _counter;
diff --git a/src/osgEarth/Revisioning.cpp b/src/osgEarth/Revisioning.cpp
index 2e416af..baba623 100644
--- a/src/osgEarth/Revisioning.cpp
+++ b/src/osgEarth/Revisioning.cpp
@@ -23,69 +23,6 @@
 
 using namespace osgEarth;
 
-//------------------------------------------------------------------------
-
-#if 0
-void
-Revisioned::dirty()
-{
-    _revision->operator++();
-
-    if ( _parents.size() > 0 )
-    {
-        for( std::vector< osg::observer_ptr<osg::Referenced> >::iterator i = _parents.begin(); i != _parents.end(); )
-        {
-            RefRevisioned* r = static_cast<RefRevisioned*>( i->get() );
-            if ( r && r->get() )
-            {
-                r->get()->dirty();
-                ++i;
-            }
-            else
-            {
-                i = _parents.erase( i );
-            }
-        }
-    }
-}
-
-
-void
-Revisioned::addParent( Revisioned* parent )
-{
-    if ( parent )
-    {
-        _parents.push_back( new RefRevisioned(parent) );
-        parent->dirty();
-    }
-}
-
-
-void
-Revisioned::removeParent( Revisioned* parent )
-{
-    for( std::vector< osg::observer_ptr<osg::Referenced> >::iterator i = _parents.begin(); i != _parents.end(); )
-    {
-        if ( i->valid() )
-        {
-            RefRevisioned* r = static_cast<RefRevisioned*>( i->get() );
-            if ( r->get() == parent )
-            {
-                i = _parents.erase( i );
-            }
-            else
-            {
-                ++i;
-            }
-        }
-        else
-        {
-            i = _parents.erase( i );
-        }
-    }
-}
-#endif
-
 
 //------------------------------------------------------------------------
 
@@ -153,6 +90,3 @@ DirtyNotifier::setDirty()
         }
     }
 }
-
-
-//------------------------------------------------------------------------
\ No newline at end of file
diff --git a/src/osgEarth/ShaderFactory b/src/osgEarth/ShaderFactory
new file mode 100644
index 0000000..7e8a429
--- /dev/null
+++ b/src/osgEarth/ShaderFactory
@@ -0,0 +1,98 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2012 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+*/
+#ifndef OSGEARTH_SHADER_FACTORY_H
+#define OSGEARTH_SHADER_FACTORY_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/VirtualProgram>
+
+namespace osgEarth
+{
+    /**
+     * A factory class that generates shader functions for the osgEarth engine.
+     * The default ShaderFactory is stored in the osgEarth registry. You can replace it
+     * if you want to replace osgEarth's default shader templates.
+     */
+    class OSGEARTH_EXPORT ShaderFactory : public osg::Referenced
+    {
+    public:
+        /** Creates a vertex shader main(). */
+        virtual osg::Shader* createVertexShaderMain(
+            const ShaderComp::FunctionLocationMap& functions =ShaderComp::FunctionLocationMap(),
+            bool useLightingShaders =true ) const;
+
+        /** Creates a fragment shader main(). */
+        virtual osg::Shader* createFragmentShaderMain( 
+            const ShaderComp::FunctionLocationMap& functions =ShaderComp::FunctionLocationMap(),
+            bool useLightingShaders =true ) const;
+
+        /**
+         * Gets the uniform/shader name of the sampler corresponding the the provider
+         * texture image unit
+         */
+        virtual std::string getSamplerName( unsigned texImageUnit ) const;
+        
+        /**
+         * Creates the function that sets up default texcoords in the vertex shader.
+         * The name/prototype is:
+         *    void osgearth_vert_setupColoring(); 
+         */
+        virtual osg::Shader* createDefaultColoringVertexShader( unsigned numTexCoordSets ) const;
+
+        /**
+         * Creates the function that applies texture data in the fragment shader.
+         * The name/prototype is:
+         *    osgearth_frag_applyColoring( inout vec4 color );
+         */
+        virtual osg::Shader* createDefaultColoringFragmentShader( unsigned numTexCoordSets ) const;
+
+        /**
+         * Creates the function that applies lighting calculations in the vertex shader.
+         * The name/prototype is:
+         *    void osgearth_vert_setupLighting();
+         */
+        virtual osg::Shader* createDefaultLightingVertexShader() const;
+
+        /**
+         * Creates the function that applies lighting coloring in the fragment shader.
+         * The name/prototype is:
+         *    void osgearth_frag_applyLighting( inout vec4 color ); 
+         */
+        virtual osg::Shader* createDefaultLightingFragmentShader() const;
+
+        /**
+         * Builds a shader that executes an image filter chain.
+         */
+        virtual osg::Shader* createColorFilterChainFragmentShader( const std::string& function, const ColorFilterChain& chain ) const;
+
+        /**
+         * Gets a uniform corresponding to the given mode and value
+         */
+        virtual osg::Uniform* createUniformForGLMode(
+            osg::StateAttribute::GLMode      mode,
+            osg::StateAttribute::GLModeValue value );
+
+        /** dtor */
+        virtual ~ShaderFactory() { }
+    };
+
+
+} // namespace osgEarth
+
+#endif // OSGEARTH_SHADER_FACTORY_H
diff --git a/src/osgEarth/ShaderFactory.cpp b/src/osgEarth/ShaderFactory.cpp
new file mode 100644
index 0000000..c61f7f3
--- /dev/null
+++ b/src/osgEarth/ShaderFactory.cpp
@@ -0,0 +1,588 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2012 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#include <osgEarth/ShaderFactory>
+
+#include <osgEarth/ShaderUtils>
+#include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
+#include <osg/Shader>
+#include <osg/Program>
+#include <osg/State>
+#include <osg/Notify>
+#include <sstream>
+
+#define LC "[ShaderFactory] "
+
+#define VERTEX_SETUP_COLORING   "osgearth_vert_setupColoring"
+#define VERTEX_SETUP_LIGHTING   "osgearth_vert_setupLighting"
+#define FRAGMENT_APPLY_COLORING "osgearth_frag_applyColoring"
+#define FRAGMENT_APPLY_LIGHTING "osgearth_frag_applyLighting"
+
+#ifdef OSG_GLES2_AVAILABLE
+#   define PRECISION_MEDIUMP_FLOAT "precision mediump float;"
+    static bool s_GLES_SHADERS = true;
+#   define GLENNS_PER_VERTEX_LIGHTING 1
+#else
+#   define PRECISION_MEDIUMP_FLOAT ""
+    static bool s_GLES_SHADERS = false;
+#   define GLENNS_PER_VERTEX_LIGHTING 1
+#endif
+
+
+using namespace osgEarth;
+using namespace osgEarth::ShaderComp;
+
+
+std::string
+ShaderFactory::getSamplerName( unsigned unit ) const
+{
+    return Stringify() << "osgearth_tex" << unit;
+}
+
+
+osg::Shader*
+ShaderFactory::createVertexShaderMain(const FunctionLocationMap& functions,
+                                      bool  useLightingShaders ) const
+{
+    FunctionLocationMap::const_iterator i = functions.find( LOCATION_VERTEX_PRE_TEXTURING );
+    const OrderedFunctionMap* preTexture = i != functions.end() ? &i->second : 0L;
+
+    FunctionLocationMap::const_iterator j = functions.find( LOCATION_VERTEX_PRE_LIGHTING );
+    const OrderedFunctionMap* preLighting = j != functions.end() ? &j->second : 0L;
+
+    FunctionLocationMap::const_iterator k = functions.find( LOCATION_VERTEX_POST_LIGHTING );
+    const OrderedFunctionMap* postLighting = k != functions.end() ? &k->second : 0L;
+
+    std::stringstream buf;
+    buf << "#version " << GLSL_VERSION_STR << "\n"
+        << PRECISION_MEDIUMP_FLOAT "\n"
+        << "void osgearth_vert_setupColoring(); \n";
+
+    if ( useLightingShaders )
+        buf << "void osgearth_vert_setupLighting(); \n";
+
+    if ( preTexture )
+        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
+            buf << "void " << i->second << "(); \n";
+
+    if ( preLighting )
+        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
+            buf << "void " << i->second << "(); \n";
+
+    if ( postLighting )
+        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
+            buf << "void " << i->second << "(); \n";
+
+    buf << "void main(void) \n"
+        << "{ \n"
+        << "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n";
+
+    if ( preTexture )
+        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
+            buf << "    " << i->second << "(); \n";
+
+    buf << "    osgearth_vert_setupColoring(); \n";
+    
+    if ( preLighting )
+        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
+            buf << "    " << i->second << "(); \n";
+
+    if ( useLightingShaders )
+        buf << "    osgearth_vert_setupLighting(); \n";
+    
+    if ( postLighting )
+        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
+            buf << "    " << i->second << "(); \n";
+
+    buf << "} \n";
+
+    std::string str;
+    str = buf.str();
+    osg::Shader* shader = new osg::Shader( osg::Shader::VERTEX, str );
+    shader->setName( "main(vert)" );
+    return shader;
+}
+
+
+osg::Shader*
+ShaderFactory::createFragmentShaderMain(const FunctionLocationMap& functions,
+                                        bool  useLightingShaders ) const
+{
+    FunctionLocationMap::const_iterator i = functions.find( LOCATION_FRAGMENT_PRE_TEXTURING );
+    const OrderedFunctionMap* preTexture = i != functions.end() ? &i->second : 0L;
+
+    FunctionLocationMap::const_iterator j = functions.find( LOCATION_FRAGMENT_PRE_LIGHTING );
+    const OrderedFunctionMap* preLighting = j != functions.end() ? &j->second : 0L;
+
+    FunctionLocationMap::const_iterator k = functions.find( LOCATION_FRAGMENT_POST_LIGHTING );
+    const OrderedFunctionMap* postLighting = k != functions.end() ? &k->second : 0L;
+
+    std::stringstream buf;
+    buf << "#version " << GLSL_VERSION_STR << "\n"
+        << PRECISION_MEDIUMP_FLOAT << "\n"
+        << "void osgearth_frag_applyColoring( inout vec4 color ); \n";
+
+    if ( useLightingShaders )
+        buf << "void osgearth_frag_applyLighting( inout vec4 color ); \n";
+
+    if ( preTexture )
+        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
+            buf << "void " << i->second << "( inout vec4 color ); \n";
+
+    if ( preLighting )
+        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
+            buf << "void " << i->second << "( inout vec4 color ); \n";
+
+    if ( postLighting )
+        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
+            buf << "void " << i->second << "( inout vec4 color ); \n";
+
+    buf << "void main(void) \n"
+        << "{ \n"
+        << "    vec4 color = vec4(1,1,1,1); \n"; //gl_Color; \n";
+
+    if ( preTexture )
+        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
+            buf << "    " << i->second << "( color ); \n";
+
+    buf << "    osgearth_frag_applyColoring( color ); \n";
+
+    if ( preLighting )
+        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
+            buf << "    " << i->second << "( color ); \n";
+    
+    if ( useLightingShaders )
+        buf << "    osgearth_frag_applyLighting( color ); \n";
+
+    if ( postLighting )
+        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
+            buf << "    " << i->second << "( color ); \n";
+
+    buf << "    gl_FragColor = color; \n"
+
+#if 0 // GW: testing logarithmic depth buffer remapping
+        << "    float A = gl_ProjectionMatrix[2].z; \n"
+        << "    float B = gl_ProjectionMatrix[3].z; \n"
+        << "    float n = -B/(1.0-A); \n"
+        << "    float f =  B/(1.0+A); \n"
+        << "    float C = 1; \n"
+        << "    gl_FragDepth = log(C*gl_FragCoord.z+1) / log(C*f+1); \n"
+#endif
+        << "} \n";  
+
+    std::string str;
+    str = buf.str();
+    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, str );
+    shader->setName( "main(frag)" );
+    return shader;
+}
+ 
+
+osg::Shader*
+ShaderFactory::createDefaultColoringVertexShader( unsigned numTexCoordSets ) const
+{
+    std::stringstream buf;
+
+    buf << 
+        "#version " << GLSL_VERSION_STR << "\n"
+        PRECISION_MEDIUMP_FLOAT "\n";
+
+    buf << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n";
+
+    buf
+        << "varying vec4 osg_FrontColor; \n"
+        << "varying vec4 osg_FrontSecondaryColor; \n"
+    
+        << "void osgearth_vert_setupColoring() \n"
+        << "{ \n"
+        << "    osg_FrontColor = gl_Color; \n"
+        << "    osg_FrontSecondaryColor = vec4(0.0); \n";
+
+    //TODO: gl_TexCoord et.al. are depcrecated so we should replace them;
+    // this approach also only support up to 8 texture coord units
+    for(unsigned i=0; i<numTexCoordSets; ++i )
+    {
+        buf << "    osg_TexCoord["<< i <<"] = gl_MultiTexCoord"<< i << "; \n";
+    }
+        
+    buf << "} \n";
+
+    std::string str;
+    str = buf.str();
+
+    osg::Shader* shader = new osg::Shader(osg::Shader::VERTEX, str);
+    shader->setName( VERTEX_SETUP_COLORING );
+    return shader;
+}
+
+
+osg::Shader*
+ShaderFactory::createDefaultColoringFragmentShader( unsigned numTexImageUnits ) const
+{
+    std::stringstream buf;
+
+    buf << "#version " << GLSL_VERSION_STR << "\n"
+        << PRECISION_MEDIUMP_FLOAT << "\n";
+    
+    buf << "varying vec4 osg_FrontColor; \n";
+    
+    if ( numTexImageUnits > 0 )
+    {
+        buf << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n";
+        buf << "uniform sampler2D ";
+        for( unsigned i=0; i<numTexImageUnits; ++i )
+        {
+            buf << getSamplerName(i) << (i+1 < numTexImageUnits? "," : "; \n");
+        }
+    }
+
+    buf << "void osgearth_frag_applyColoring( inout vec4 color ) \n"
+        << "{ \n"
+        << "    color = color * osg_FrontColor; \n";
+    
+    if ( numTexImageUnits > 0 )
+    {
+        buf << "    vec4 texel; \n";
+
+        for(unsigned i=0; i<numTexImageUnits; ++i )
+        {
+            buf << "    texel = texture2D(" << getSamplerName(i) << ", osg_TexCoord["<< i <<"].st); \n";
+            buf << "    color.rgb = mix( color.rgb, texel.rgb, texel.a ); \n";
+            if ( i == 0 )
+                buf << "    color.a = texel.a * color.a; \n";
+        }
+    }
+
+    buf << "} \n";
+
+    std::string str;
+    str = buf.str();
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, str );
+    shader->setName( FRAGMENT_APPLY_COLORING );
+    return shader;
+}
+
+#ifdef GLENNS_PER_VERTEX_LIGHTING
+
+osg::Shader*
+ShaderFactory::createDefaultLightingVertexShader() const
+{
+    std::string str = Stringify() << 
+
+        "#version " GLSL_VERSION_STR "\n"
+        PRECISION_MEDIUMP_FLOAT "\n"
+
+        "uniform bool oe_mode_GL_LIGHTING; \n"
+        "varying vec4 oe_lighting_adjustment; \n"
+        "varying vec4 oe_zero_vec; \n"
+
+        "void osgearth_vert_setupLighting() \n"
+        "{ \n"
+        "    oe_lighting_adjustment = vec4(1.0); \n"
+        "    if (oe_mode_GL_LIGHTING) \n"
+        "    { \n"
+        "        vec3 N = normalize(gl_NormalMatrix * gl_Normal); \n"
+        "        float NdotL = dot( N, normalize(gl_LightSource[0].position.xyz) ); \n"
+        "        NdotL = max( 0.0, NdotL ); \n"
+        "        oe_zero_vec = vec4(0.0); \n"
+        "        vec4 adj = \n"
+        //"            gl_FrontLightModelProduct.sceneColor + \n" // not available in GLES yet
+        "            gl_FrontLightProduct[0].ambient + \n"
+        "            gl_FrontLightProduct[0].diffuse * NdotL; \n"
+        "        oe_lighting_adjustment = clamp( adj, 0.0, 1.0 ); \n"
+        "    } \n"
+        "} \n";
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::VERTEX, str );
+    shader->setName( VERTEX_SETUP_LIGHTING );
+    return shader;
+}
+
+
+osg::Shader*
+ShaderFactory::createDefaultLightingFragmentShader() const
+{
+    std::string str = Stringify() <<
+
+        "#version " GLSL_VERSION_STR "\n"
+        PRECISION_MEDIUMP_FLOAT "\n"
+
+        "varying vec4 oe_lighting_adjustment; \n"
+        "varying vec4 oe_zero_vec; \n"
+
+         "uniform bool oe_mode_GL_LIGHTING; \n"
+         "void osgearth_frag_applyLighting( inout vec4 color ) \n"
+         "{ \n"
+         "    if ( oe_mode_GL_LIGHTING ) \n"
+         "    { \n"
+         "        float alpha = color.a; \n"
+         "        color = color * oe_lighting_adjustment + oe_zero_vec; \n"
+         "        color.a = alpha; \n"
+         "    } \n"
+        "} \n";
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, str );
+    shader->setName( FRAGMENT_APPLY_LIGHTING );
+    return shader;
+}
+
+#endif
+
+
+#ifdef GLENNS_PER_FRAGMENT_LIGHTING // does not work on GLES - unresolved
+
+osg::Shader*
+ShaderFactory::createDefaultLightingVertexShader() const
+{
+    std::string str = Stringify() << 
+
+        "#version " GLSL_VERSION_STR "\n"
+        PRECISION_MEDIUMP_FLOAT "\n"
+
+        "uniform bool oe_mode_GL_LIGHTING; \n"
+        "varying vec3 oe_lighting_normal; \n"
+
+        "void osgearth_vert_setupLighting() \n"
+        "{ \n"
+        "    if (oe_mode_GL_LIGHTING) \n"
+        "    { \n"
+        "        oe_lighting_normal = normalize(gl_NormalMatrix * gl_Normal); \n"
+        "    } \n"
+        "} \n";
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::VERTEX, str );
+    shader->setName( VERTEX_SETUP_LIGHTING );
+    return shader;
+}
+
+
+osg::Shader*
+ShaderFactory::createDefaultLightingFragmentShader() const
+{
+    std::string str = Stringify() <<
+
+        "#version " GLSL_VERSION_STR "\n"
+        PRECISION_MEDIUMP_FLOAT "\n"
+
+        "uniform bool oe_mode_GL_LIGHTING; \n"
+        "varying vec3 oe_lighting_normal; \n"
+
+         "void osgearth_frag_applyLighting( inout vec4 color ) \n"
+         "{ \n"
+         "    if ( oe_mode_GL_LIGHTING ) \n"
+         "    { \n"
+         "        float alpha = color.a; \n"
+         "        vec3 n = normalize( oe_lighting_normal ); \n"
+         "        float NdotL = dot( n, normalize(gl_LightSource[0].position.xyz) ); \n"
+         "        NdotL = max( 0.0, NdotL ); \n"
+         "        vec4 adjustment = \n"
+         //"            gl_FrontLightModelProduct.sceneColor + \n" // not available in GLES yet
+         "            gl_FrontLightProduct[0].ambient + \n"
+         "            gl_FrontLightProduct[0].diffuse * NdotL; \n"
+         "        color *= clamp(adjustment, 0.0, 1.0); \n"
+
+         // specular highlights: (skip them for now)
+         //"        float NdotHV = dot( n, gl_LightSource[0].halfVector.xyz ); \n"
+         //"        NdotHV = max( 0.0, NdotHV ); \n"
+         //"        if ( NdotL * NdotHV > 0.0 ) \n"
+         //"            color += gl_FrontLightProduct[0].specular * \n"
+         //"                     pow( NdotHV, gl_FrontMaterial.shininess ); \n"
+
+         "        color.a = alpha; \n"
+         "    } \n"
+        "} \n";
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, str );
+    shader->setName( FRAGMENT_APPLY_LIGHTING );
+    return shader;
+}
+
+#endif // GLENNS_PER_FRAGMENT_LIGHTING
+
+
+#ifdef TOMS_PER_VERTEX_LIGHTING
+
+osg::Shader*
+ShaderFactory::createDefaultLightingVertexShader() const
+{
+    int maxLights = Registry::capabilities().getMaxLights();
+    
+    std::stringstream buf;
+    buf << "#version " << GLSL_VERSION_STR << "\n";
+
+    if ( s_GLES_SHADERS )
+    {
+        buf << "precision mediump float;\n"
+            << osg_LightSourceParameters::glslDefinition() << "\n"
+            << osg_LightProducts::glslDefinition() << "\n"
+            << "uniform osg_LightSourceParameters osg_LightSource0;\n"
+            << "uniform osg_LightProducts osg_FrontLightProduct0;\n";
+    }
+    
+    buf
+        << "varying vec4 osg_FrontColor; \n"
+        << "varying vec4 osg_FrontSecondaryColor; \n"
+        << "uniform bool oe_mode_GL_LIGHTING; \n";
+    
+    if ( s_GLES_SHADERS )
+    {
+        buf
+            << "void osgearth_vert_setupLighting() \n"
+            << "{ \n"
+            << "    if (oe_mode_GL_LIGHTING) \n"
+            << "    { \n"
+            << "        float shine = 10.0;\n"
+            << "        vec4 lightModelAmbi = vec4(0.1,0.1,0.1,1.0);\n"
+            //gl_FrontMaterial.shininess
+            //gl_LightModel.ambient
+            << "        vec3 normal = gl_NormalMatrix * gl_Normal; \n"
+            << "        float NdotL = dot( normal, normalize(osg_LightSource0.position.xyz) ); \n"
+            << "        NdotL = max( 0.0, NdotL ); \n"
+            << "        float NdotHV = dot( normal, osg_LightSource0.halfVector.xyz ); \n"
+            << "        NdotHV = max( 0.0, NdotHV ); \n"
+            
+            << "        osg_FrontColor.rgb = osg_FrontColor.rgb * \n"
+            << "            clamp( \n"
+            << "                lightModelAmbi + \n"
+            << "                osg_FrontLightProduct0.ambient +          \n"
+            << "                osg_FrontLightProduct0.diffuse * NdotL, 0.0, 1.0).rgb;   \n"
+            
+            << "        osg_FrontSecondaryColor = vec4(0.0); \n"
+            
+            << "        if ( NdotL * NdotHV > 0.0 ) \n"
+            << "        { \n"
+            << "            osg_FrontSecondaryColor.rgb = (osg_FrontLightProduct0.specular * \n"
+            << "                                          pow( NdotHV, shine )).rgb;\n"
+            << "        } \n"
+            << "    } \n"
+            << "} \n";
+    }
+    else // !s_GLES_SHADERS
+    {
+        buf
+            << "void osgearth_vert_setupLighting() \n"
+            << "{ \n"
+            << "    if (oe_mode_GL_LIGHTING) \n"
+            << "    { \n"
+            << "        vec3 normal = gl_NormalMatrix * gl_Normal; \n"
+            << "        float NdotL = dot( normal, normalize(gl_LightSource[0].position.xyz) ); \n"
+            << "        NdotL = max( 0.0, NdotL ); \n"
+            << "        float NdotHV = dot( normal, gl_LightSource[0].halfVector.xyz ); \n"
+            << "        NdotHV = max( 0.0, NdotHV ); \n"
+
+            << "        osg_FrontColor.rgb = osg_FrontColor.rgb * \n"
+            << "            clamp( \n"
+            << "                gl_LightModel.ambient + \n"
+            << "                gl_FrontLightProduct[0].ambient +          \n"
+            << "                gl_FrontLightProduct[0].diffuse * NdotL, 0.0, 1.0).rgb;   \n"
+
+            << "        osg_FrontSecondaryColor = vec4(0.0); \n"
+            << "        if ( NdotL * NdotHV > 0.0 ) \n"
+            << "        { \n"
+            << "            osg_FrontSecondaryColor.rgb = (gl_FrontLightProduct[0].specular * \n"
+            << "                                          pow( NdotHV, gl_FrontMaterial.shininess )).rgb;\n"
+            << "        } \n"
+            << "    } \n"
+            << "} \n";
+    }
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::VERTEX, buf.str().c_str() );
+    shader->setName( VERTEX_SETUP_LIGHTING );
+    return shader;
+}
+
+
+osg::Shader*
+ShaderFactory::createDefaultLightingFragmentShader() const
+{
+    std::stringstream buf;
+    
+    buf << "#version " << GLSL_VERSION_STR << "\n"
+        << PRECISION_MEDIUMP_FLOAT << "\n"
+    
+    << "varying vec4 osg_FrontColor; \n"
+    << "varying vec4 osg_FrontSecondaryColor; \n"
+    
+    << "uniform bool oe_mode_GL_LIGHTING; \n"
+    << "void osgearth_frag_applyLighting( inout vec4 color ) \n"
+    << "{ \n"
+    << "    if ( oe_mode_GL_LIGHTING ) \n"
+    << "    { \n"
+    << "        float alpha = color.a; \n"
+    << "        color = (color * osg_FrontColor) + osg_FrontSecondaryColor; \n"
+    << "        color.a = alpha; \n"
+    << "    } \n"
+    << "} \n";
+
+    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, buf.str().c_str() );
+    shader->setName( FRAGMENT_APPLY_LIGHTING );
+    return shader;
+}
+
+#endif // TOMS_PER_VERTEX_LIGHTING
+
+
+osg::Shader*
+ShaderFactory::createColorFilterChainFragmentShader( const std::string& function, const ColorFilterChain& chain ) const
+{
+    std::stringstream buf;
+    buf << "#version " << GLSL_VERSION_STR << "\n"
+        << PRECISION_MEDIUMP_FLOAT << "\n";
+
+    // write out the shader function prototypes:
+    for( ColorFilterChain::const_iterator i = chain.begin(); i != chain.end(); ++i )
+    {
+        ColorFilter* filter = i->get();
+        buf << "void " << filter->getEntryPointFunctionName() << "(in int slot, inout vec4 color);\n";
+    }
+
+    // write out the main function:
+    buf << "void " << function << "(in int slot, inout vec4 color) \n"
+        << "{ \n";
+
+    // write out the function calls. if there are none, it's a NOP.
+    for( ColorFilterChain::const_iterator i = chain.begin(); i != chain.end(); ++i )
+    {
+        ColorFilter* filter = i->get();
+        buf << "    " << filter->getEntryPointFunctionName() << "(slot, color);\n";
+    }
+        
+    buf << "} \n";
+
+    std::string bufstr;
+    bufstr = buf.str();
+    return new osg::Shader(osg::Shader::FRAGMENT, bufstr);
+}
+
+
+osg::Uniform*
+ShaderFactory::createUniformForGLMode(osg::StateAttribute::GLMode      mode,
+                                      osg::StateAttribute::GLModeValue value)
+{
+    osg::Uniform* u = 0L;
+
+    if ( mode == GL_LIGHTING )
+    {
+        osg::Uniform* u = new osg::Uniform(osg::Uniform::BOOL, "oe_mode_GL_LIGHTING");
+        u->set( (value & osg::StateAttribute::ON) != 0 );
+    }
+
+    return u;
+}
diff --git a/src/osgEarth/ShaderGenerator b/src/osgEarth/ShaderGenerator
index 4bd0346..51fcafd 100644
--- a/src/osgEarth/ShaderGenerator
+++ b/src/osgEarth/ShaderGenerator
@@ -21,9 +21,10 @@
 #define OSGEARTH_SHADER_GENERATOR_H 1
 
 #include <osgEarth/Common>
+#include <osgEarth/StateSetCache>
+#include <osgEarth/VirtualProgram>
 #include <osg/NodeVisitor>
 #include <osg/State>
-#include <osgEarth/StateSetCache>
 
 namespace osgEarth
 {
@@ -46,6 +47,7 @@ namespace osgEarth
          */
         ShaderGenerator( StateSetCache* cache );
 
+        /** dtor. */
         virtual ~ShaderGenerator() { }
 
 
@@ -59,11 +61,15 @@ namespace osgEarth
 
         void apply( osg::Drawable* );
 
-        bool generate( osg::StateSet* stateSet, osg::ref_ptr<osg::StateSet>& replacement );
+        bool processGeometry( osg::StateSet* stateSet, osg::ref_ptr<osg::StateSet>& replacement );
+
+        bool processText( osg::StateSet* stateSet, osg::ref_ptr<osg::StateSet>& replacement );
 
         osg::ref_ptr<osg::State> _state;
 
         osg::ref_ptr<StateSetCache> _stateSetCache;
+
+        osg::ref_ptr<VirtualProgram> _defaultVP;
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/ShaderGenerator.cpp b/src/osgEarth/ShaderGenerator.cpp
index 5e1de73..17419c7 100644
--- a/src/osgEarth/ShaderGenerator.cpp
+++ b/src/osgEarth/ShaderGenerator.cpp
@@ -17,11 +17,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 
+#include <osgEarth/Capabilities>
+#include <osgEarth/Registry>
+#include <osgEarth/ShaderFactory>
 #include <osgEarth/ShaderGenerator>
-#include <osgEarth/ShaderComposition>
 #include <osgEarth/StringUtils>
-#include <osgEarth/Registry>
-#include <osgEarth/Capabilities>
 
 #include <osg/Drawable>
 #include <osg/Geode>
@@ -29,6 +29,7 @@
 #include <osg/Texture2D>
 #include <osg/Texture3D>
 #include <osg/TexEnv>
+#include <osgText/Text>
 
 #define LC "[ShaderGenerator] "
 
@@ -51,10 +52,12 @@ using namespace osgEarth;
 #endif
 
 // shader names
-#define TEX_COORD    "oe_sg_texcoord"
-#define SAMPLER      "oe_sg_sampler"
-#define ATTRIB       "oe_sg_attrib"
-#define TEXENV_COLOR "oe_sg_texenvcolor"
+#define TEX_COORD      "oe_sg_texcoord"
+#define TEX_COORD_TEXT "oe_sg_texcoord_text"
+#define SAMPLER        "oe_sg_sampler"
+#define SAMPLER_TEXT   "oe_sg_sampler_text"
+#define ATTRIB         "oe_sg_attrib"
+#define TEXENV_COLOR   "oe_sg_texenvcolor"
 
 #define VERTEX_FUNCTION   "oe_sg_vert"
 #define FRAGMENT_FUNCTION "oe_sg_frag"
@@ -136,6 +139,8 @@ osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN )
 {
     _state = new StateEx();
     _stateSetCache = new StateSetCache();
+    _defaultVP = new VirtualProgram();
+    _defaultVP->installDefaultColoringAndLightingShaders();
 }
 
 
@@ -144,6 +149,8 @@ osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN )
 {
     _state = new StateEx();
     _stateSetCache = cache ? cache : new StateSetCache();
+    _defaultVP = new VirtualProgram();
+    _defaultVP->installDefaultColoringAndLightingShaders();
 }
 
 
@@ -156,7 +163,7 @@ ShaderGenerator::apply( osg::Node& node )
         _state->pushStateSet( ss.get() );
 
         osg::ref_ptr<osg::StateSet> replacement;
-        if ( generate(ss.get(), replacement) )
+        if ( processGeometry(ss.get(), replacement) )
         {
             _state->popStateSet();
             node.setStateSet( replacement.get() );
@@ -182,7 +189,7 @@ ShaderGenerator::apply( osg::Geode& geode )
         _state->pushStateSet( ss.get() );
 
         osg::ref_ptr<osg::StateSet> replacement;
-        if ( generate(ss.get(), replacement) )
+        if ( processGeometry(ss.get(), replacement) )
         {
             _state->popStateSet();
             geode.setStateSet( replacement.get() );
@@ -211,11 +218,22 @@ ShaderGenerator::apply( osg::Drawable* drawable )
         if ( ss.valid() )
         {
             _state->pushStateSet(ss.get());
-            
+
             osg::ref_ptr<osg::StateSet> replacement;
-            if ( generate(ss.get(), replacement) )
+
+            if ( dynamic_cast<osgText::Text*>(drawable) != 0L )
+            {
+                if ( processText(ss.get(), replacement) )
+                {
+                    drawable->setStateSet( replacement.get() );
+                }
+            }
+            else
             {
-                drawable->setStateSet(replacement.get());
+                if ( processGeometry(ss.get(), replacement) )
+                {
+                    drawable->setStateSet(replacement.get());
+                }
             }
 
             _state->popStateSet();
@@ -225,7 +243,7 @@ ShaderGenerator::apply( osg::Drawable* drawable )
 
 
 bool
-ShaderGenerator::generate( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& replacement )
+ShaderGenerator::processText( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& replacement )
 {
     // do nothing if there's no GLSL support
     if ( !Registry::capabilities().supportsGLSL() )
@@ -240,28 +258,89 @@ ShaderGenerator::generate( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& repla
     if ( dynamic_cast<osg::Program*>(program) != 0L )
         return false;
 
-    // New stateset that we'll merge with the existing one.
-    osg::ref_ptr<osg::StateSet> newStateSet = new osg::StateSet();
+    // see if the current state set contains a VirtualProgram already. If so,
+    // we will add to it if necessary.
+    VirtualProgram* vp = dynamic_cast<VirtualProgram*>( ss->getAttribute(VirtualProgram::SA_TYPE) );
+
+    replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);
+
+    std::string vertSrc =
+        "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION "\n"
+        "varying " MEDIUMP "vec4 " TEX_COORD_TEXT ";\n"
+        "void " VERTEX_FUNCTION "()\n"
+        "{ \n"
+        INDENT TEX_COORD_TEXT " = gl_MultiTexCoord0;\n"
+        "} \n";
+
+    std::string fragSrc =
+        "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION "\n"
+        "uniform sampler2D " SAMPLER_TEXT ";\n"
+        "varying " MEDIUMP "vec4 " TEX_COORD_TEXT ";\n"
+        "void " FRAGMENT_FUNCTION "(inout vec4 color)\n"
+        "{ \n"
+        INDENT MEDIUMP "vec4 texel = texture2D(" SAMPLER_TEXT ", " TEX_COORD_TEXT ".xy);\n"
+        INDENT "color.a *= texel.a; \n"
+        "}\n";
+
+    if ( !vp )
+        vp = osg::clone( _defaultVP.get() );
+    replacement->setAttributeAndModes( vp, osg::StateAttribute::ON );
+
+    vp->setUseLightingShaders( false );
+    vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
+    vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+    replacement->getOrCreateUniform( SAMPLER_TEXT, osg::Uniform::SAMPLER_2D )->set( 0 );
+
+    return replacement.valid();
+}
+
+
+bool
+ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& replacement )
+{
+    // do nothing if there's no GLSL support
+    if ( !Registry::capabilities().supportsGLSL() )
+        return false;
+
+    // State object with extra accessors:
+    StateEx* state = static_cast<StateEx*>(_state.get());
+
+    // check for a real osg::Program in the whole state stack. If it exists, bail out
+    // so that OSG can use the program already in the graph. We never override a
+    // full Program.
+    osg::StateAttribute* program = state->getAttribute(osg::StateAttribute::PROGRAM);
+    if ( dynamic_cast<osg::Program*>(program) != 0L )
+        return false;
+
+    // see if the current state set contains a VirtualProgram already. If so,
+    // we will add to it if necessary.
+    VirtualProgram* vp = dynamic_cast<VirtualProgram*>( ss->getAttribute(VirtualProgram::SA_TYPE) );
 
-    // check whether the lighting state has changed.
+    // Check whether the lighting state has changed and install a mode uniform.
     if ( ss->getMode(GL_LIGHTING) != osg::StateAttribute::INHERIT )
     {
+        if ( !replacement.valid() ) 
+            replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);
+
+        ShaderFactory* sf = Registry::instance()->getShaderFactory();
         osg::StateAttribute::GLModeValue value = state->getMode(GL_LIGHTING); // from the state, not the ss.
-        osg::Uniform* lighting = newStateSet->getOrCreateUniform( "osgearth_LightingEnabled", osg::Uniform::BOOL );
-        lighting->set( (value & osg::StateAttribute::ON) != 0 );
+        replacement->addUniform( sf->createUniformForGLMode(GL_LIGHTING, value) );
     }
 
     // if the stateset changes any texture attributes, we need a new virtual program:
-    if ( ss->getTextureAttributeList().size() > 0 )
+    if (ss->getTextureAttributeList().size() > 0)
     {
+        if ( !replacement.valid() ) 
+            replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);
+
         // work off the state's accumulated texture attribute set:
         int texCount = state->getNumTextureAttributes();
 
-        // check for an existing VirtualProgram; if found, we'll add to it.
-        // if not, we'll make a new one.
-        osg::ref_ptr<VirtualProgram> vp = dynamic_cast<VirtualProgram*>(program);
-        if ( !vp.valid() )
-            vp = new VirtualProgram();
+        if ( !vp )
+        {
+            vp = osg::clone( _defaultVP.get() );
+            replacement->setAttributeAndModes( vp, osg::StateAttribute::ON );
+        }
 
         // start generating the shader source.
         std::stringstream vertHead, vertBody, fragHead, fragBody;
@@ -277,9 +356,10 @@ ShaderGenerator::generate( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& repla
 
         for( int t = 0; t < texCount; ++t )
         {
-            //todo: consider TexEnv for DECAL/MODULATE
-
-            fragBody << INDENT << MEDIUMP "vec4 texel; \n";
+            if (t == 0)
+            {
+                fragBody << INDENT << MEDIUMP "vec4 texel; \n";
+            }
 
             osg::StateAttribute* tex = state->getTextureAttribute( t, osg::StateAttribute::TEXTURE );
             if ( tex )
@@ -292,7 +372,7 @@ ShaderGenerator::generate( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& repla
                     blendingMode = env->getMode();
                     if ( blendingMode == osg::TexEnv::BLEND )
                     {
-                        newStateSet->getOrCreateUniform( Stringify() << TEXENV_COLOR << t, osg::Uniform::FLOAT_VEC4 )->set( env->getColor() );
+                        replacement->getOrCreateUniform( Stringify() << TEXENV_COLOR << t, osg::Uniform::FLOAT_VEC4 )->set( env->getColor() );
                     }
                 }
 
@@ -305,19 +385,19 @@ ShaderGenerator::generate( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& repla
                 {
                     fragHead << "uniform sampler1D " SAMPLER << t << ";\n";
                     fragBody << INDENT "texel = texture1D(" SAMPLER << t << ", " TEX_COORD << t << ".x);\n";
-                    newStateSet->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_1D )->set( t );
+                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_1D )->set( t );
                 }
                 else if ( dynamic_cast<osg::Texture2D*>(tex) )
                 {
                     fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
                     fragBody << INDENT "texel = texture2D(" SAMPLER << t << ", " TEX_COORD << t << ".xy);\n";
-                    newStateSet->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
+                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
                 }
                 else if ( dynamic_cast<osg::Texture3D*>(tex) )
                 {
                     fragHead << "uniform sampler3D " SAMPLER << t << ";\n";
                     fragBody << INDENT "texel = texture3D(" SAMPLER << t << ", " TEX_COORD << t << ".xyz);\n";
-                    newStateSet->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_3D )->set( t );
+                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_3D )->set( t );
                 }
 
                 // See http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml
@@ -367,23 +447,7 @@ ShaderGenerator::generate( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& repla
         // inject the shaders:
         vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
         vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
-
-        // optimize sharing of VPs.
-        newStateSet->setAttributeAndModes( vp.get(), osg::StateAttribute::ON );
-
-        //osg::ref_ptr<osg::StateAttribute> sharedVP;
-        //_stateSetCache->share( vp.get(), sharedVP );
-        //newStateSet->setAttributeAndModes( sharedVP.get(), osg::StateAttribute::ON );
     }
 
-    // pop the current stateset off the stack so we can change it:
-    osg::ref_ptr<osg::StateSet> current = osg::clone( ss );
-
-    // merge in the new state set:
-    current->merge( *newStateSet.get() );
-
-    // optimize sharing:
-    _stateSetCache->share(current, replacement);
-
-    return true;
+    return replacement.valid();
 }
diff --git a/src/osgEarth/ShaderUtils b/src/osgEarth/ShaderUtils
index 39e938e..b59b64f 100644
--- a/src/osgEarth/ShaderUtils
+++ b/src/osgEarth/ShaderUtils
@@ -30,22 +30,36 @@
 namespace osgEarth
 {
     /**
+     * ShaderPolicy encodes general behavior when deciding how to
+     * employ shaders in certain situations
+     */
+    enum ShaderPolicy
+    {
+        SHADERPOLICY_DISABLE,
+        SHADERPOLICY_GENERATE,
+        SHADERPOLICY_INHERIT
+    };
+
+
+    /**
     * Container for light uniforms
     */
     //light product
-    class osg_LightProducts 
+    struct osg_LightProducts 
     { 
-    public:
         osg_LightProducts(int id);
         
         osg::ref_ptr<osg::Uniform> ambient; // vec4 
         osg::ref_ptr<osg::Uniform> diffuse; // vec4
         osg::ref_ptr<osg::Uniform> specular; //vec4
+
+        // GLSL strings
+        static std::string glslDefinition();
     };
-        
-    class osg_LightSourceParameters 
+
+
+    struct osg_LightSourceParameters 
     { 
-    public:
         osg_LightSourceParameters(int id);
         
         void setUniformsFromOsgLight(const osg::Light* light, osg::Matrix viewMatrix, const osg::Material* frontMat);
@@ -57,17 +71,30 @@ namespace osgEarth
         osg::ref_ptr<osg::Uniform>  position; // vec4
         osg::ref_ptr<osg::Uniform>  halfVector; // vec4 
         osg::ref_ptr<osg::Uniform>  spotDirection; // vec3 
-        osg::ref_ptr<osg::Uniform> spotExponent; // float
-        osg::ref_ptr<osg::Uniform> spotCutoff; // float
-        osg::ref_ptr<osg::Uniform> spotCosCutoff; // float
+        osg::ref_ptr<osg::Uniform>  spotExponent; // float
+        osg::ref_ptr<osg::Uniform>  spotCutoff; // float
+        osg::ref_ptr<osg::Uniform>  spotCosCutoff; // float
         osg::ref_ptr<osg::Uniform>  constantAttenuation; // float 
         osg::ref_ptr<osg::Uniform>  linearAttenuation; // float
         osg::ref_ptr<osg::Uniform>  quadraticAttenuation; // float
 
         //just store the light product in here
         osg_LightProducts _frontLightProduct;
+
+        // GLSL strings
+        static std::string glslDefinition();
+    };
+
+    /**
+     * Preprocesses GLES shader source to include our osg_LightProducts and osg_LightSourceParameters
+     * definitions and uniforms.
+     */
+    class OSGEARTH_EXPORT ShaderPreProcessor
+    {
+    public:
+        static void run(osg::Shader* shader);
     };
-    
+
     /**
      * A callback that will update the osgEarth lighting uniforms (based on the
      * FFP lighting state) if necessary.
diff --git a/src/osgEarth/ShaderUtils.cpp b/src/osgEarth/ShaderUtils.cpp
index fac88e2..4402220 100644
--- a/src/osgEarth/ShaderUtils.cpp
+++ b/src/osgEarth/ShaderUtils.cpp
@@ -28,6 +28,13 @@ using namespace osgEarth;
 
 namespace 
 {
+#if !defined(OSG_GL_FIXED_FUNCTION_AVAILABLE)
+    static bool s_NO_FFP = true;
+#else
+    static bool s_NO_FFP = false;
+#endif
+
+
     typedef std::list<const osg::StateSet*> StateSetStack;
 
     static osg::StateAttribute::GLModeValue 
@@ -122,10 +129,98 @@ namespace
 
 //------------------------------------------------------------------------
 
+namespace State_Utils
+{
+    // Code borrowed from osg::State.cpp
+    bool replace(std::string& str, const std::string& original_phrase, const std::string& new_phrase)
+    {
+        bool replacedStr = false;
+        std::string::size_type pos = 0;
+        while((pos=str.find(original_phrase, pos))!=std::string::npos)
+        {
+            std::string::size_type endOfPhrasePos = pos+original_phrase.size();
+            if (endOfPhrasePos<str.size())
+            {
+                char c = str[endOfPhrasePos];
+                if ((c>='0' && c<='9') ||
+                    (c>='a' && c<='z') ||
+                    (c>='A' && c<='Z') ||
+                    (c==']'))
+                {
+                    pos = endOfPhrasePos;
+                    continue;
+                }
+            }
+
+            replacedStr = true;
+            str.replace(pos, original_phrase.size(), new_phrase);
+        }
+        return replacedStr;
+    }
+
+    void replaceAndInsertDeclaration(std::string& source, std::string::size_type declPos, const std::string& originalStr, const std::string& newStr, const std::string& declarationPrefix, const std::string& declarationSuffix ="")
+    {
+        if (replace(source, originalStr, newStr))
+        {
+            source.insert(declPos, declarationPrefix + newStr + declarationSuffix + std::string(";\n"));
+        }
+    }
+}
+
+void
+ShaderPreProcessor::run(osg::Shader* shader)
+{
+    // only runs for non-FFP (GLES, GL3+, etc.)
+
+    if ( s_NO_FFP && shader )
+    {
+        std::string source = shader->getShaderSource();
+
+        // find the first legal insertion point for replacement declarations. GLSL requires that nothing
+        // precede a "#verson" compiler directive, so we must insert new declarations after it.
+        std::string::size_type declPos = source.rfind( "#version " );
+        if ( declPos != std::string::npos )
+        {
+            // found the string, now find the next linefeed and set the insertion point after it.
+            declPos = source.find( '\n', declPos );
+            declPos = declPos != std::string::npos ? declPos+1 : source.length();
+        }
+        else
+        {
+            declPos = 0;
+        }
+
+        int maxLights = Registry::capabilities().getMaxLights();
+
+        for( int i=0; i<maxLights; ++i )
+        {
+            State_Utils::replaceAndInsertDeclaration(
+                source, declPos,
+                Stringify() << "gl_LightSource[" << i << "]",
+                Stringify() << "osg_LightSource" << i,
+                Stringify() 
+                    << osg_LightSourceParameters::glslDefinition() << "\n"
+                    << "uniform osg_LightSourceParameters " );
+
+            State_Utils::replaceAndInsertDeclaration(
+                source, declPos,
+                Stringify() << "gl_FrontLightProduct[" << i << "]", 
+                Stringify() << "osg_FrontLightProduct" << i,
+                Stringify()
+                    << osg_LightProducts::glslDefinition() << "\n"
+                    << "uniform osg_LightProducts " );
+        }
+
+        shader->setShaderSource( source );
+    }
+}
+
+//------------------------------------------------------------------------
+
 osg_LightProducts::osg_LightProducts(int id)
 {
     std::stringstream uniNameStream;
-    uniNameStream << "osg_FrontLightProduct[" << id << "]";
+    uniNameStream << "osg_FrontLightProduct" << id; //[" << id << "]";
     std::string uniName = uniNameStream.str();
 
     ambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, uniName+".ambient"); // vec4
@@ -133,11 +228,24 @@ osg_LightProducts::osg_LightProducts(int id)
     specular = new osg::Uniform(osg::Uniform::FLOAT_VEC4, uniName+".specular"); // vec4
 }
 
+std::string 
+osg_LightProducts::glslDefinition()
+{
+    return
+        "struct osg_LightProducts {"
+        " vec4 ambient;"
+        " vec4 diffuse;"
+        " vec4 specular;"
+        " };";
+}
+
+//------------------------------------------------------------------------
+
 osg_LightSourceParameters::osg_LightSourceParameters(int id)
     : _frontLightProduct(id)
 {
     std::stringstream uniNameStream;
-    uniNameStream << "osg_LightSource[" << id << "]";
+    uniNameStream << "osg_LightSource" << id; // [" << id << "]";
     std::string uniName = uniNameStream.str();
     
     ambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, uniName+".ambient"); // vec4
@@ -224,6 +332,28 @@ void osg_LightSourceParameters::applyState(osg::StateSet* stateset)
     stateset->addUniform(_frontLightProduct.specular.get());
 }
 
+std::string
+osg_LightSourceParameters::glslDefinition()
+{
+    return
+        "struct osg_LightSourceParameters {"
+        " vec4 ambient;"
+        " vec4 diffuse;"
+        " vec4 specular;"
+        " vec4 position;"
+        " vec4 halfVector;"
+        " vec3 spotDirection;"
+        " float spotExponent;"
+        " float spotCutoff;"
+        " float spotCosCutoff;"
+        " float constantAttenuation;"
+        " float linearAttenuation;"
+        " float quadraticAttenuation;"
+        " };";
+}
+
+//------------------------------------------------------------------------
+
 
 #undef LC
 #define LC "[UpdateLightingUniformHelper] "
@@ -238,17 +368,17 @@ _useUpdateTrav  ( useUpdateTrav )
 
     _lightEnabled = new bool[ _maxLights ];
     if ( _maxLights > 0 ){
-        _lightEnabled[0] = 1;
+        _lightEnabled[0] = true;
         //allocate light
         _osgLightSourceParameters.push_back(osg_LightSourceParameters(0));
     }
     for(int i=1; i<_maxLights; ++i ){
-        _lightEnabled[i] = 0;
+        _lightEnabled[i] = true;
         _osgLightSourceParameters.push_back(osg_LightSourceParameters(i));
     }
 
-    _lightingEnabledUniform = new osg::Uniform( osg::Uniform::BOOL, "osgearth_LightingEnabled" );
-    _lightEnabledUniform    = new osg::Uniform( osg::Uniform::INT,  "osgearth_LightEnabled", _maxLights );
+    _lightingEnabledUniform = new osg::Uniform( osg::Uniform::BOOL, "oe_mode_GL_LIGHTING" );
+    _lightEnabledUniform    = new osg::Uniform( osg::Uniform::BOOL, "oe_mode_GL_LIGHT", _maxLights );
 
     if ( !_useUpdateTrav )
     {
@@ -286,7 +416,7 @@ UpdateLightingUniformsHelper::cullTraverse( osg::Node* node, osg::NodeVisitor* n
         // Update the overall lighting-enabled value:
         bool lightingEnabled =
             ( getModeValue(stateSetStack, GL_LIGHTING) & osg::StateAttribute::ON ) != 0;
-        
+
         if ( lightingEnabled != _lightingEnabled || !_applied )
         {
             _lightingEnabled = lightingEnabled;
@@ -296,30 +426,50 @@ UpdateLightingUniformsHelper::cullTraverse( osg::Node* node, osg::NodeVisitor* n
                 _lightingEnabledUniform->set( _lightingEnabled );
         }
 
-        // Update the list of enabled lights:
-        for( int i=0; i < _maxLights; ++i )
+        osg::View* view = cv->getCurrentCamera()->getView();
+        if ( view )
         {
-            bool enabled =
-                ( getModeValue( stateSetStack, GL_LIGHT0 + i ) & osg::StateAttribute::ON ) != 0;
-            
-            const osg::Light* light = getLightByID(stateSetStack, i);
-            const osg::Material* material = getFrontMaterial(stateSetStack);
+            osg::Light* light = view->getLight();
+            if ( light )
+            {
+                const osg::Material* material = getFrontMaterial(stateSetStack);
+                _osgLightSourceParameters[0].setUniformsFromOsgLight(light, cv->getCurrentCamera()->getViewMatrix(), material);
+            }
+        }
 
-            if ( _lightEnabled[i] != enabled || !_applied )
+        else
+        {
+            // Update the list of enabled lights:
+            for( int i=0; i < _maxLights; ++i )
             {
-                _lightEnabled[i] = enabled;
-                if ( _useUpdateTrav ){
-                    _dirty = true;
-                }else{
-                    _lightEnabledUniform->setElement( i, _lightEnabled[i] );
+                bool enabled =
+                    ( getModeValue( stateSetStack, GL_LIGHT0 + i ) & osg::StateAttribute::ON ) != 0;
+                
+                const osg::Light* light = getLightByID(stateSetStack, i);
+                const osg::Material* material = getFrontMaterial(stateSetStack);
+
+                if ( light )
+                {
+                    OE_NOTICE << "Found Light " << i << std::endl;
                 }
-            }
-            
-            //update light position info regardsless of if applied for now
-            if(light){
-                _osgLightSourceParameters[i].setUniformsFromOsgLight(light, cv->getCurrentCamera()->getViewMatrix(), material);
-            }
-        }	
+
+                if ( _lightEnabled[i] != enabled || !_applied )
+                {
+                    _lightEnabled[i] = enabled;
+                    if ( _useUpdateTrav ){
+                        _dirty = true;
+                    }else{
+                        _lightEnabledUniform->setElement( i, _lightEnabled[i] );
+                    }
+                }
+                
+                //update light position info regardsless of if applied for now
+                if(light){
+                    OE_NOTICE << "Setting light source params." << std::endl;
+                    _osgLightSourceParameters[i].setUniformsFromOsgLight(light, cv->getCurrentCamera()->getViewMatrix(), material);
+                }
+            }	
+        }
 
         // apply if necessary:
         if ( !_applied && !_useUpdateTrav )
diff --git a/src/osgEarth/StateSetCache b/src/osgEarth/StateSetCache
index 1f0f857..d55be5c 100644
--- a/src/osgEarth/StateSetCache
+++ b/src/osgEarth/StateSetCache
@@ -36,6 +36,16 @@ namespace osgEarth
         virtual ~StateSetCache() { }
 
         /**
+         * Check whether a StateSet is eligible for sharing.
+         */
+        bool eligible( osg::StateSet* stateSet ) const;
+
+        /**
+         * Check whether a StateAttribute is eligible for sharing.
+         */
+        bool eligible( osg::StateAttribute* attr ) const;
+
+        /**
          * Traverse the node and consolidate equivalent state sets, updating
          * the cache along the way.
          */
@@ -50,7 +60,8 @@ namespace osgEarth
          */
         bool share( 
             osg::ref_ptr<osg::StateSet>& input, 
-            osg::ref_ptr<osg::StateSet>& output );
+            osg::ref_ptr<osg::StateSet>& output,
+            bool                         checkEligible =true );
 
         /**
          * Looks in the attribute cache for an attribute matching the input.
@@ -61,13 +72,19 @@ namespace osgEarth
          */
         bool share(
             osg::ref_ptr<osg::StateAttribute>& input,
-            osg::ref_ptr<osg::StateAttribute>& output );
+            osg::ref_ptr<osg::StateAttribute>& output,
+            bool                               checkEligible =true );
 
         /**
          * Number of statesets in the cache.
          */
         unsigned size() const { return _stateSetCache.size(); }
 
+        /**
+         * Clears out the cache.
+         */
+        void clear();
+
     protected: 
         struct CompareStateSets {
             bool operator()(
diff --git a/src/osgEarth/StateSetCache.cpp b/src/osgEarth/StateSetCache.cpp
index 47c947b..610bf9b 100644
--- a/src/osgEarth/StateSetCache.cpp
+++ b/src/osgEarth/StateSetCache.cpp
@@ -19,6 +19,7 @@
 #include <osgEarth/StateSetCache>
 #include <osg/NodeVisitor>
 #include <osg/Geode>
+#include <osg/BufferIndexBinding>
 
 #define LC "[StateSetCache] "
 
@@ -172,23 +173,76 @@ StateSetCache::optimize(osg::Node* node)
 
 
 bool
-StateSetCache::share(osg::ref_ptr<osg::StateSet>& input,
-                     osg::ref_ptr<osg::StateSet>& output )
+StateSetCache::eligible(osg::StateSet* stateSet) const
 {
-    Threading::ScopedMutexLock lock( _mutex );
+    if ( !stateSet )
+        return false;
 
-    std::pair<StateSetSet::iterator,bool> result = _stateSetCache.insert( input );
-    if ( result.second )
+    // DYNAMIC means the user intends to change it later. So it needs to
+    // stay independent.
+    if ( stateSet->getDataVariance() == osg::Object::DYNAMIC )
+        return false;
+
+    const osg::StateSet::AttributeList& attrs = stateSet->getAttributeList();
+    for( osg::StateSet::AttributeList::const_iterator i = attrs.begin(); i != attrs.end(); ++i )
     {
-        // first use
-        output = input.get();
+        osg::StateAttribute* a = i->second.first.get();
+        if ( !eligible(a) )
+            return false;
+    }
+
+    return true;
+}
+
+
+bool
+StateSetCache::eligible(osg::StateAttribute* attr) const
+{
+    if ( !attr )
         return false;
+
+    // DYNAMIC means the user intends to change it later. So it needs to
+    // stay independent.
+    if ( attr->getDataVariance() == osg::Object::DYNAMIC )
+        return false;
+
+    // cannot share BIB's. They don't clone well since they have underlying buffer objects
+    // that may be in use. It results in OpenGL invalid enumerant errors and errors such as
+    // "uniform block xxx has no binding"
+    if (dynamic_cast<osg::BufferIndexBinding*>(attr) != 0L)
+        return false;
+
+    return true;
+}
+
+
+bool
+StateSetCache::share(osg::ref_ptr<osg::StateSet>& input,
+                     osg::ref_ptr<osg::StateSet>& output,
+                     bool                         checkEligible)
+{
+    if ( !checkEligible || eligible(input.get()) )
+    {
+        Threading::ScopedMutexLock lock( _mutex );
+
+        std::pair<StateSetSet::iterator,bool> result = _stateSetCache.insert( input );
+        if ( result.second )
+        {
+            // first use
+            output = input.get();
+            return false;
+        }
+        else
+        {
+            // found a share!
+            output = result.first->get();
+            return true;
+        }
     }
     else
     {
-        // found a share!
-        output = result.first->get();
-        return true;
+        output = input.get();
+        return false;
     }
 }
 
@@ -196,21 +250,40 @@ StateSetCache::share(osg::ref_ptr<osg::StateSet>& input,
 
 bool
 StateSetCache::share(osg::ref_ptr<osg::StateAttribute>& input,
-                     osg::ref_ptr<osg::StateAttribute>& output)
+                     osg::ref_ptr<osg::StateAttribute>& output,
+                     bool                               checkEligible)
 {
-    Threading::ScopedMutexLock lock( _mutex );
-
-    std::pair<StateAttributeSet::iterator,bool> result = _stateAttributeCache.insert( input );
-    if ( result.second )
+    if ( !checkEligible || eligible(input.get()) )
     {
-        // first use
-        output = input.get();
-        return false;
+        Threading::ScopedMutexLock lock( _mutex );
+
+        std::pair<StateAttributeSet::iterator,bool> result = _stateAttributeCache.insert( input );
+        if ( result.second )
+        {
+            // first use
+            output = input.get();
+            return false;
+        }
+        else
+        {
+            // found a share!
+            output = result.first->get();
+            return true;
+        }
     }
     else
     {
-        // found a share!
-        output = result.first->get();
-        return true;
+        output = input.get();
+        return false;
     }
 }
+
+
+void
+StateSetCache::clear()
+{
+    Threading::ScopedMutexLock lock( _mutex );
+
+    _stateAttributeCache.clear();
+    _stateSetCache.clear();
+}
diff --git a/src/osgEarth/Terrain b/src/osgEarth/Terrain
index 685ccb0..1aa276c 100644
--- a/src/osgEarth/Terrain
+++ b/src/osgEarth/Terrain
@@ -95,6 +95,9 @@ namespace osgEarth
             double                  y,
             double*                 out_heightAboveMSL,
             double*                 out_heightAboveEllipsoid =0L) const =0;
+
+        /** dtor */
+        virtual ~TerrainHeightProvider() { }
     };
 
 
diff --git a/src/osgEarth/TerrainEngineNode b/src/osgEarth/TerrainEngineNode
index 611ef04..a474b3b 100644
--- a/src/osgEarth/TerrainEngineNode
+++ b/src/osgEarth/TerrainEngineNode
@@ -20,6 +20,7 @@
 #define OSGEARTH_TERRAIN_ENGINE_NODE_H 1
 
 #include <osgEarth/Map>
+#include <osgEarth/MapFrame>
 #include <osgEarth/Terrain>
 #include <osgEarth/TextureCompositor>
 #include <osgEarth/ShaderUtils>
diff --git a/src/osgEarth/TerrainEngineNode.cpp b/src/osgEarth/TerrainEngineNode.cpp
index 72908b9..b80cc6d 100644
--- a/src/osgEarth/TerrainEngineNode.cpp
+++ b/src/osgEarth/TerrainEngineNode.cpp
@@ -21,6 +21,7 @@
 #include <osgEarth/Registry>
 #include <osgEarth/TextureCompositor>
 #include <osgEarth/NodeUtils>
+#include <osgEarth/MapModelChange>
 #include <osgDB/ReadFile>
 #include <osg/CullFace>
 #include <osg/PolygonOffset>
@@ -353,7 +354,7 @@ TerrainEngineNode::updateImageUniforms()
         // layer count has changed, but the shader has not yet caught up. In the future we might use this to disable
         // "ghost" layers that used to exist at a given index, but no longer do.
         
-        _imageLayerController->_layerVisibleUniform.attach( "osgearth_ImageLayerVisible", osg::Uniform::BOOL,  stateSet, MAX_IMAGE_LAYERS );
+        _imageLayerController->_layerVisibleUniform.attach( "osgearth_ImageLayerVisible", osg::Uniform::BOOL,  stateSet, mapf.imageLayers().size() );
         _imageLayerController->_layerOpacityUniform.attach( "osgearth_ImageLayerOpacity", osg::Uniform::FLOAT, stateSet, mapf.imageLayers().size() );
         _imageLayerController->_layerRangeUniform.attach  ( "osgearth_ImageLayerRange",   osg::Uniform::FLOAT, stateSet, 2 * mapf.imageLayers().size() );
 
@@ -429,7 +430,7 @@ TerrainEngineNode::traverse( osg::NodeVisitor& nv )
         }
 
 
-        if ( Registry::instance()->getCapabilities().supportsGLSL() )
+        if ( Registry::capabilities().supportsGLSL() )
         {
             _updateLightingUniformsHelper.cullTraverse( this, &nv );
 
diff --git a/src/osgEarth/TerrainLayer.cpp b/src/osgEarth/TerrainLayer.cpp
index 4df7302..fa6528e 100644
--- a/src/osgEarth/TerrainLayer.cpp
+++ b/src/osgEarth/TerrainLayer.cpp
@@ -587,8 +587,8 @@ TerrainLayer::initTileSource()
 
 bool
 TerrainLayer::isKeyValid(const TileKey& key) const
-{
-    if (!key.valid() || _tileSourceInitFailed) 
+{    
+    if (!key.valid())
         return false;
 
     // Check to see if an explicity max LOD is set. Do NOT compare against the minLevel,
@@ -650,31 +650,25 @@ TerrainLayer::setDBOptions( const osgDB::Options* dbOptions )
 void
 TerrainLayer::initializeCachePolicy( const osgDB::Options* options )
 {
-    // establish this layer's cache policy.
+    optional<CachePolicy> cp;
+
     if ( _initOptions.cachePolicy().isSet() )
     {
-        // if this layer defines its own CP, use it.
-        setCachePolicy( *_initOptions.cachePolicy() );
+        // if the initialization options specify a cache policy, attempt to use it
+        osg::ref_ptr<osgDB::Options> temp = Registry::instance()->cloneOrCreateOptions(options);
+        _initOptions.cachePolicy()->apply( temp.get() );
+
+        if ( ! Registry::instance()->getCachePolicy(cp, temp.get()) )
+            cp = CachePolicy::DEFAULT;
     }
-    else
+    else 
     {
-        // see if the new DBOptions has one set; if so, inherit that:
-        optional<CachePolicy> incomingCP;
-        if ( CachePolicy::fromOptions(options, incomingCP) )
-        {
-            setCachePolicy( incomingCP.get() );
-        }
-        else if ( Registry::instance()->defaultCachePolicy().isSet() )
-        {
-            // not set, no try to inherit from the registry:
-            setCachePolicy( Registry::instance()->defaultCachePolicy().value() );
-        }
-        else
-        {
-            // not found anywhere; set to the default.
-            setCachePolicy( CachePolicy::DEFAULT );
-        }
+        // otherwise go the normal route.
+        if ( ! Registry::instance()->getCachePolicy(cp, options) )
+            cp = CachePolicy::DEFAULT;
     }
+
+    setCachePolicy( *cp );
 }
 
 void
diff --git a/src/osgEarth/TerrainOptions b/src/osgEarth/TerrainOptions
index 16303c2..c0c3e68 100644
--- a/src/osgEarth/TerrainOptions
+++ b/src/osgEarth/TerrainOptions
@@ -145,14 +145,7 @@ namespace osgEarth
          * Default = 6.0.
          */
         optional<float>& minTileRangeFactor() { return _minTileRangeFactor; }
-        const optional<float>& minTileRangeFactor() const { return _minTileRangeFactor; }
-
-        /**
-         * Whether to monitor terrain tile boundaries and match up the vertices.
-         * Default = false.
-         */
-        optional<bool>& normalizeEdges() { return _normalizeEdges; }
-        const optional<bool>& normalizeEdges() const { return _normalizeEdges; }
+        const optional<float>& minTileRangeFactor() const { return _minTileRangeFactor; }        
 
         /**
          * Whether to apply the default layer combination logic using TexEnvCombine.
@@ -241,14 +234,14 @@ namespace osgEarth
         /**
          * The min filter to be applied to textures
          */
-        optional<osg::Texture::FilterMode> minFilter() { return _minFilter;}
-        const optional<osg::Texture::FilterMode> minFilter() const { return _minFilter;}
+        optional<osg::Texture::FilterMode>& minFilter() { return _minFilter;}
+        const optional<osg::Texture::FilterMode>& minFilter() const { return _minFilter;}
 
         /**
          * The mag filter to be applied to textures
          */
-        optional<osg::Texture::FilterMode> magFilter() { return _magFilter;}
-        const optional<osg::Texture::FilterMode> magFilter() const { return _magFilter;}
+        optional<osg::Texture::FilterMode>& magFilter() { return _magFilter;}
+        const optional<osg::Texture::FilterMode>& magFilter() const { return _magFilter;}
             
         /**
          * Whether to enable blending
@@ -294,8 +287,7 @@ namespace osgEarth
                     
         optional<float> _verticalScale;
         optional<float> _heightFieldSampleRatio;
-        optional<float> _minTileRangeFactor;
-        optional<bool> _normalizeEdges;
+        optional<float> _minTileRangeFactor;        
         optional<bool> _combineLayers;
         optional<LoadingPolicy> _loadingPolicy;
         optional<CompositingTechnique> _compositingTech;
diff --git a/src/osgEarth/TerrainOptions.cpp b/src/osgEarth/TerrainOptions.cpp
index fb3e678..0788469 100644
--- a/src/osgEarth/TerrainOptions.cpp
+++ b/src/osgEarth/TerrainOptions.cpp
@@ -90,7 +90,6 @@ DriverConfigOptions( options ),
 _verticalScale( 1.0f ),
 _heightFieldSampleRatio( 1.0f ),
 _minTileRangeFactor( 6.0 ),
-_normalizeEdges( false ),
 _combineLayers( true ),
 _loadingPolicy( LoadingPolicy() ),
 _compositingTech( COMPOSITING_AUTO ),
@@ -125,8 +124,7 @@ TerrainOptions::getConfig() const
 
     conf.updateObjIfSet( "loading_policy", _loadingPolicy );
     conf.updateIfSet( "vertical_scale", _verticalScale );
-    conf.updateIfSet( "min_tile_range_factor", _minTileRangeFactor );
-    conf.updateIfSet( "normalize_edges", _normalizeEdges );
+    conf.updateIfSet( "min_tile_range_factor", _minTileRangeFactor );    
     conf.updateIfSet( "combine_layers", _combineLayers );
     conf.updateIfSet( "max_lod", _maxLOD );
     conf.updateIfSet( "min_lod", _minLOD );
@@ -173,8 +171,7 @@ TerrainOptions::fromConfig( const Config& conf )
 
     conf.getObjIfSet( "loading_policy", _loadingPolicy );
     conf.getIfSet( "vertical_scale", _verticalScale );
-    conf.getIfSet( "min_tile_range_factor", _minTileRangeFactor );
-    conf.getIfSet( "normalize_edges", _normalizeEdges );
+    conf.getIfSet( "min_tile_range_factor", _minTileRangeFactor );    
     conf.getIfSet( "combine_layers", _combineLayers );
     conf.getIfSet( "max_lod", _maxLOD ); conf.getIfSet( "max_level", _maxLOD );
     conf.getIfSet( "min_lod", _minLOD ); conf.getIfSet( "min_level", _minLOD );
diff --git a/src/osgEarth/TextureCompositor b/src/osgEarth/TextureCompositor
index fde89d1..509ee19 100644
--- a/src/osgEarth/TextureCompositor
+++ b/src/osgEarth/TextureCompositor
@@ -26,8 +26,6 @@
 #include <osg/StateSet>
 #include <osg/Program>
 
-#define MAX_IMAGE_LAYERS 16
-
 namespace osgEarth
 {
     class ImageLayer;
diff --git a/src/osgEarth/TextureCompositor.cpp b/src/osgEarth/TextureCompositor.cpp
index 2803689..960b1bc 100644
--- a/src/osgEarth/TextureCompositor.cpp
+++ b/src/osgEarth/TextureCompositor.cpp
@@ -24,6 +24,7 @@
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/Map>
+#include <osgEarth/MapModelChange>
 #include <osg/Texture2DArray>
 #include <osg/Texture2D>
 #include <osg/Texture3D>
diff --git a/src/osgEarth/TextureCompositorMulti.cpp b/src/osgEarth/TextureCompositorMulti.cpp
index 1f7f5f2..b1cd1ad 100644
--- a/src/osgEarth/TextureCompositorMulti.cpp
+++ b/src/osgEarth/TextureCompositorMulti.cpp
@@ -19,7 +19,7 @@
 #include <osgEarth/TextureCompositorMulti>
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/ShaderUtils>
 #include <osgEarth/TileKey>
 #include <osgEarth/StringUtils>
@@ -51,17 +51,20 @@ namespace
 
         buf << "#version " << GLSL_VERSION_STR << "\n";
 
-        buf << "varying vec4 osg_FrontColor;\n"
-            << "varying vec4 osg_FrontSecondaryColor;\n";
+        buf << "varying vec4 osg_FrontColor; \n"
+            << "varying vec4 osg_FrontSecondaryColor; \n";
+
+        // NOTE: always use the "max GPU coord sets" for texture coordinates 
+        // to support proper shader merging under GLES. -gw
 
         if ( slots.size() > 0 )
         {
-            buf << "varying vec4 osg_TexCoord[" << Registry::instance()->getCapabilities().getMaxGPUTextureCoordSets()  << "];\n";
+            buf << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets()  << "]; \n";
         }
 
         if ( blending )
         {
-            buf << "uniform mat4 osgearth_TexBlendMatrix[" << Registry::instance()->getCapabilities().getMaxGPUTextureCoordSets() << "];\n";
+            buf << "uniform mat4 osgearth_TexBlendMatrix[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n";
         }
 
         buf << "void osgearth_vert_setupColoring() \n"
@@ -82,12 +85,12 @@ namespace
                 if ( slot == primarySlot )
                 {
                     // normal unit:
-                    buf << "    osg_TexCoord["<< slot <<"] = gl_MultiTexCoord" << slot << ";\n";
+                    buf << "    osg_TexCoord["<< slot <<"] = gl_MultiTexCoord" << slot << "; \n";
                 }
                 else
                 {
                     // secondary (blending) unit:
-                    buf << "    osg_TexCoord["<< slot <<"] = osgearth_TexBlendMatrix["<< primarySlot << "] * gl_MultiTexCoord" << primarySlot << ";\n";
+                    buf << "    osg_TexCoord["<< slot <<"] = osgearth_TexBlendMatrix["<< primarySlot << "] * gl_MultiTexCoord" << primarySlot << "; \n";
                 }
             }
         }
@@ -111,11 +114,6 @@ namespace
         buf << "precision mediump float;\n";
 #endif
 
-        if ( maxSlots > 0 )
-        {
-            buf << "varying vec4 osg_TexCoord[" << Registry::instance()->getCapabilities().getMaxGPUTextureCoordSets() << "];\n";
-        }
-
         if ( blending )
         {
             buf << "#extension GL_ARB_shader_texture_lod : enable \n"
@@ -124,12 +122,15 @@ namespace
                 << "uniform float osgearth_LODRangeFactor; \n";
         }
 
-        buf << "uniform float osgearth_ImageLayerOpacity[" << maxSlots << "]; \n"
-            //The enabled array is a fixed size.  Make sure this corresponds EXCATLY to the size definition in TerrainEngineNode.cpp
-            << "uniform bool  osgearth_ImageLayerVisible[" << MAX_IMAGE_LAYERS << "]; \n"
-            << "uniform float osgearth_ImageLayerRange[" << 2 * maxSlots << "]; \n"
-            << "uniform float osgearth_ImageLayerAttenuation; \n"
-            << "uniform float osgearth_CameraElevation; \n";
+        if ( maxSlots > 0 )
+        {
+            buf << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n"
+                << "uniform float osgearth_ImageLayerOpacity[" << maxSlots << "]; \n"
+                << "uniform bool  osgearth_ImageLayerVisible[" << maxSlots << "]; \n"
+                << "uniform float osgearth_ImageLayerRange[" << 2 * maxSlots << "]; \n"
+                << "uniform float osgearth_ImageLayerAttenuation; \n"
+                << "uniform float osgearth_CameraElevation; \n";
+        }
 
         const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots();
 
@@ -300,25 +301,21 @@ namespace
 bool
 TextureCompositorMultiTexture::isSupported( bool useGPU )
 {
-    const Capabilities& caps = osgEarth::Registry::instance()->getCapabilities();
+    const Capabilities& caps = osgEarth::Registry::capabilities();
     if ( useGPU )
-#ifndef OSG_GLES2_AVAILABLE
-        return caps.supportsGLSL( 1.10f ) && caps.supportsMultiTexture();
-#else
-        return caps.supportsGLSL( 1.0f ) && caps.supportsMultiTexture();
-#endif
+        return caps.supportsGLSL() && caps.supportsMultiTexture();
     else
         return caps.supportsMultiTexture();
 }
 
 TextureCompositorMultiTexture::TextureCompositorMultiTexture( bool useGPU, const TerrainOptions& options ) :
 _lodTransitionTime( *options.lodTransitionTime() ),
-_enableMipmapping( *options.enableMipmapping() ),
-_minFilter( *options.minFilter() ),
-_magFilter( *options.magFilter() ),
-_useGPU( useGPU )
+_enableMipmapping ( *options.enableMipmapping() ),
+_minFilter        ( *options.minFilter() ),
+_magFilter        ( *options.magFilter() ),
+_useGPU           ( useGPU )
 {
-    _enableMipmappingOnUpdatedTextures = Registry::instance()->getCapabilities().supportsMipmappedTextureUpdates();
+    _enableMipmappingOnUpdatedTextures = Registry::capabilities().supportsMipmappedTextureUpdates();
 }
 
 void
@@ -403,24 +400,21 @@ TextureCompositorMultiTexture::updateMasterStateSet(osg::StateSet*       stateSe
         }
 
         VirtualProgram* vp = static_cast<VirtualProgram*>( stateSet->getAttribute(VirtualProgram::SA_TYPE) );
-        if ( maxUnits > 0 )
-        {
-            // see if we have any blended layers:
-            bool hasBlending = layout.containsSecondarySlots( maxUnits );
+        // see if we have any blended layers:
+        bool hasBlending = layout.containsSecondarySlots( maxUnits );
 
-            vp->setShader(
-                "osgearth_vert_setupColoring",
-                s_createTextureVertexShader(layout, hasBlending) );
+        // Why are these marked as PROTECTED? See the comments in MapNode.cpp for the answer.
+        // (Where it sets up the top-level VirtualProgram)
 
-            vp->setShader(
-                "osgearth_frag_applyColoring",
-                s_createTextureFragShaderFunction(layout, maxUnits, hasBlending, _lodTransitionTime ) );
-        }
-        else
-        {
-            vp->removeShader( "osgearth_frag_applyColoring" );
-            vp->removeShader( "osgearth_vert_setupColoring" );
-        }
+        vp->setShader(
+            "osgearth_vert_setupColoring",
+            s_createTextureVertexShader(layout, hasBlending),
+            osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+
+        vp->setShader(
+            "osgearth_frag_applyColoring",
+            s_createTextureFragShaderFunction(layout, maxUnits, hasBlending, _lodTransitionTime),
+            osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
     }
 
     else
@@ -524,22 +518,29 @@ TextureCompositorMultiTexture::createSamplerFunction(UID layerUID,
     int slot = layout.getSlot( layerUID );
     if ( slot >= 0 )
     {
-        std::stringstream buf;
-
-        buf << "uniform sampler2D "<< makeSamplerName(slot) << "; \n"
-            << "vec4 " << functionName << "() \n"
-            << "{ \n";
+        std::string src;
 
         if ( type == osg::Shader::VERTEX )
-            buf << "    return texture2D("<< makeSamplerName(slot) << ", gl_MultiTexCoord"<< slot <<".st); \n";
-        else
-            buf << "    return texture2D("<< makeSamplerName(slot) << ", gl_TexCoord["<< slot << "].st); \n";
-
-        buf << "} \n";
+        {
+            src = Stringify()
+                << "uniform sampler2D "<< makeSamplerName(slot) << "; \n"
+                << "vec4 " << functionName << "() \n"
+                << "{ \n"
+                << "    return texture2D("<< makeSamplerName(slot) << ", gl_MultiTexCoord"<< slot <<".st); \n"
+                << "} \n";
+        }
+        else // if ( type != osg::Shader::FRAGMENT )
+        {
+            src = Stringify()
+                << "uniform sampler2D "<< makeSamplerName(slot) << "; \n"
+                << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets()  << "]; \n"
+                << "vec4 " << functionName << "() \n"
+                << "{ \n"
+                << "    return texture2D("<< makeSamplerName(slot) << ", osg_TexCoord["<< slot << "].st); \n"
+                << "} \n";
+        }
 
-        std::string str;
-        str = buf.str();
-        result = new osg::Shader( type, str );
+        result = new osg::Shader( type, src );
     }
     return result;
 }
diff --git a/src/osgEarth/TextureCompositorTexArray b/src/osgEarth/TextureCompositorTexArray
index b973920..6279dcb 100644
--- a/src/osgEarth/TextureCompositorTexArray
+++ b/src/osgEarth/TextureCompositorTexArray
@@ -76,6 +76,8 @@ namespace osgEarth
 
     private:
         float _lodTransitionTime;
+        osg::Texture::FilterMode _minFilter;
+        osg::Texture::FilterMode _magFilter;
     };
 }
 
diff --git a/src/osgEarth/TextureCompositorTexArray.cpp b/src/osgEarth/TextureCompositorTexArray.cpp
index 843abbe..4d02923 100644
--- a/src/osgEarth/TextureCompositorTexArray.cpp
+++ b/src/osgEarth/TextureCompositorTexArray.cpp
@@ -25,7 +25,7 @@
 
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/SparseTexture2DArray>
 #include <osgEarth/ShaderUtils>
 #include <osgEarth/TileKey>
@@ -184,7 +184,7 @@ namespace
 {
     osg::Texture2DArray*
     s_getTexture( osg::StateSet* stateSet, const TextureLayout& layout,
-                  int unit, unsigned textureSize )
+                  int unit, unsigned textureSize, osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter )
     {
         osg::Texture2DArray* tex = static_cast<osg::Texture2DArray*>(
             stateSet->getTextureAttribute( unit, osg::StateAttribute::TEXTURE ) );
@@ -201,8 +201,8 @@ namespace
             // configure the mipmapping
             tex->setMaxAnisotropy(16.0f);
             tex->setResizeNonPowerOfTwoHint(false);
-            tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
-            tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR );
+            tex->setFilter( osg::Texture::MAG_FILTER, magFilter );
+            tex->setFilter( osg::Texture::MIN_FILTER, minFilter );
 
             // configure the wrapping
             tex->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE);
@@ -290,7 +290,9 @@ TextureCompositorTexArray::isSupported()
 }
 
 TextureCompositorTexArray::TextureCompositorTexArray( const TerrainOptions& options ) :
-_lodTransitionTime( *options.lodTransitionTime() )
+_lodTransitionTime( *options.lodTransitionTime() ),
+_minFilter        ( *options.minFilter() ),
+_magFilter        ( *options.magFilter() )
 {
     //nop
 }
@@ -341,7 +343,7 @@ TextureCompositorTexArray::applyLayerUpdate(osg::StateSet*       stateSet,
 
     // access the texture array, creating or growing it if necessary:
     osg::Texture2DArray* texture = s_getTexture( stateSet, layout, 0,
-                                                 textureSize() );
+                                                 textureSize(), _minFilter, _magFilter);
     ensureSampler( stateSet, 0 );
     // assign the new image at the proper position in the texture array.
     osg::Image* image = preparedImage.getImage();
@@ -421,11 +423,13 @@ TextureCompositorTexArray::updateMasterStateSet( osg::StateSet* stateSet, const
 
     vp->setShader(
         "osgearth_vert_setupColoring",
-        s_createTextureVertSetupShaderFunction(layout) );
+        s_createTextureVertSetupShaderFunction(layout),
+        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
 
     vp->setShader( 
         "osgearth_frag_applyColoring", 
-        s_createTextureFragShaderFunction(layout, true, _lodTransitionTime ) );
+        s_createTextureFragShaderFunction(layout, true, _lodTransitionTime ),
+        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
 }
 
 osg::Shader*
diff --git a/src/osgEarth/TileKey b/src/osgEarth/TileKey
index 9c74cfd..588d88c 100644
--- a/src/osgEarth/TileKey
+++ b/src/osgEarth/TileKey
@@ -31,6 +31,7 @@ namespace osgEarth
 {
     /**
      * Uniquely identifies a single tile on the map, relative to a Profile.
+     * Profiles have an origin of 0,0 at the top left.
      */
     class OSGEARTH_EXPORT TileKey
     {
@@ -124,10 +125,10 @@ namespace osgEarth
 
         /**
          * Creates a key that represents this tile's neighbor at the same LOD. Wraps
-         * around in X and Y automatically.
+         * around in X and Y automatically. For example, xoffset=-1 yoffset=1 will
+         * give you the key for the tile SW of this tile.
          */
-        enum Direction { NORTH, SOUTH, EAST, WEST };
-        TileKey createNeighborKey( Direction dir ) const;
+        TileKey createNeighborKey( int xoffset, int yoffset ) const;
 
         /**
          * Gets the level of detail of the tile represented by this key.
diff --git a/src/osgEarth/TileKey.cpp b/src/osgEarth/TileKey.cpp
index 42e1c28..a6360f5 100644
--- a/src/osgEarth/TileKey.cpp
+++ b/src/osgEarth/TileKey.cpp
@@ -152,20 +152,24 @@ TileKey::createAncestorKey( int ancestorLod ) const
 }
 
 TileKey
-TileKey::createNeighborKey( TileKey::Direction dir ) const
+TileKey::createNeighborKey( int xoffset, int yoffset ) const
 {
-    unsigned int tx, ty;
+    unsigned tx, ty;
     getProfile()->getNumTiles( _lod, tx, ty );
 
-    unsigned int x =
-        dir == WEST ? _x > 0 ? _x-1 : tx-1 :
-        dir == EAST ? _x+1 < tx ? _x+1 : 0 :
-        _x;
+    int sx = (int)_x + xoffset;
+    unsigned x =
+        sx < 0        ? (unsigned)((int)tx + sx) :
+        sx >= (int)tx ? (unsigned)sx - tx :
+        (unsigned)sx;
 
-    unsigned int y = 
-        dir == SOUTH ? _y > 0 ? _y-1 : ty-1 :
-        dir == NORTH ? _y+1 < ty ? _y+1 : 0 :
-        _y;        
+    int sy = (int)_y + yoffset;
+    unsigned y =
+        sy < 0        ? (unsigned)((int)ty + sy) :
+        sy >= (int)ty ? (unsigned)sy - ty :
+        (unsigned)sy;
+
+    //OE_NOTICE << "Returning neighbor " << x << ", " << y << " for tile " << str() << " offset=" << xoffset << ", " << yoffset << std::endl;
 
     return TileKey( _lod, x, y, _profile.get() );
 }
diff --git a/src/osgEarth/TileSource b/src/osgEarth/TileSource
index 72c927d..a629d43 100644
--- a/src/osgEarth/TileSource
+++ b/src/osgEarth/TileSource
@@ -57,7 +57,7 @@ namespace osgEarth
     /**
      * Configuration options for a tile source driver.
      */
-    class OSGEARTH_EXPORT TileSourceOptions : public DriverConfigOptions // no export; header only
+    class OSGEARTH_EXPORT TileSourceOptions : public DriverConfigOptions
     {
     public:
 
@@ -82,6 +82,9 @@ namespace osgEarth
         optional<int>& L2CacheSize() { return _L2CacheSize; }
         const optional<int>& L2CacheSize() const { return _L2CacheSize; }
 
+        optional<bool>& bilinearReprojection() { return _bilinearReprojection; }
+        const optional<bool>& bilinearReprojection() const { return _bilinearReprojection; }
+
     public:
         TileSourceOptions( const ConfigOptions& options =ConfigOptions() );
 
@@ -102,6 +105,7 @@ namespace osgEarth
         optional<ProfileOptions> _profileOptions;
         optional<std::string>    _blacklistFilename;
         optional<int>            _L2CacheSize;
+        optional<bool>           _bilinearReprojection;
     };
 
     typedef std::vector<TileSourceOptions> TileSourceOptionsVector;
diff --git a/src/osgEarth/TileSource.cpp b/src/osgEarth/TileSource.cpp
index ca8030e..dd64a4f 100644
--- a/src/osgEarth/TileSource.cpp
+++ b/src/osgEarth/TileSource.cpp
@@ -144,12 +144,13 @@ TileBlacklist::write(std::ostream &output) const
 
 
 TileSourceOptions::TileSourceOptions( const ConfigOptions& options ) :
-DriverConfigOptions( options ),
-_tileSize          ( 256 ),
-_noDataValue       ( (float)SHRT_MIN ),
-_noDataMinValue    ( -32000.0f ),
-_noDataMaxValue    (  32000.0f ),
-_L2CacheSize       ( 16 )
+DriverConfigOptions   ( options ),
+_tileSize             ( 256 ),
+_noDataValue          ( (float)SHRT_MIN ),
+_noDataMinValue       ( -32000.0f ),
+_noDataMaxValue       (  32000.0f ),
+_L2CacheSize          ( 16 ),
+_bilinearReprojection ( true )
 { 
     fromConfig( _conf );
 }
@@ -165,6 +166,7 @@ TileSourceOptions::getConfig() const
     conf.updateIfSet( "nodata_max", _noDataMaxValue );
     conf.updateIfSet( "blacklist_filename", _blacklistFilename);
     conf.updateIfSet( "l2_cache_size", _L2CacheSize );
+    conf.updateIfSet( "bilinear_reprojection", _bilinearReprojection );
     conf.updateObjIfSet( "profile", _profileOptions );
     return conf;
 }
@@ -187,6 +189,7 @@ TileSourceOptions::fromConfig( const Config& conf )
     conf.getIfSet( "nodata_max", _noDataMaxValue );
     conf.getIfSet( "blacklist_filename", _blacklistFilename);
     conf.getIfSet( "l2_cache_size", _L2CacheSize );
+    conf.getIfSet( "bilinear_reprojection", _bilinearReprojection );
     conf.getObjIfSet( "profile", _profileOptions );
 
     // special handling of default tile size:
diff --git a/src/osgEarth/URI b/src/osgEarth/URI
index 7263063..c8ad898 100644
--- a/src/osgEarth/URI
+++ b/src/osgEarth/URI
@@ -341,6 +341,31 @@ namespace osgEarth
         }
     };
 
+
+//------------------------------------------------------------------------
+
+    /**
+     * You can install a post-read callback in a osgDB::Options and the URI
+     * class till invoke it on a ReadResult before returning.
+     */
+    class /*header-only*/ URIPostReadCallback : public osg::Referenced
+    {
+    public:
+        URIPostReadCallback() { }
+        virtual ~URIPostReadCallback() { }
+
+        virtual void operator()( ReadResult& result ) =0;
+
+    public:
+        void apply(osgDB::Options* options) {
+            if ( options ) options->setPluginData("osgEarth::URIPostReadCallback", this);
+        }
+
+        static URIPostReadCallback* from(const osgDB::Options* options) {
+            return options ? static_cast<URIPostReadCallback*>(const_cast<osgDB::Options*>(options)->getPluginData("osgEarth::URIPostReadCallback")) : 0L;
+        }
+    };
+
     
 //------------------------------------------------------------------------
 
diff --git a/src/osgEarth/URI.cpp b/src/osgEarth/URI.cpp
index ddbb576..d90c9fe 100644
--- a/src/osgEarth/URI.cpp
+++ b/src/osgEarth/URI.cpp
@@ -340,134 +340,147 @@ namespace
     {
         ReadResult result;
 
-        if ( inputURI.empty() )
-            return result;
-
-        // establish our IO options:
-        const osgDB::Options* localOptions = dbOptions ? dbOptions : Registry::instance()->getDefaultOptions();
-
-        READ_FUNCTOR reader;
-
-        URI uri = inputURI;
-
-        // check if there's an alias map, and if so, attempt to resolve the alias:
-        URIAliasMap* aliasMap = URIAliasMap::from( localOptions );
-        if ( aliasMap )
+        if ( !inputURI.empty() )
         {
-            uri = aliasMap->resolve(inputURI.full(), inputURI.context());
-        }
+            // establish our IO options:
+            const osgDB::Options* localOptions = dbOptions ? dbOptions : Registry::instance()->getDefaultOptions();
 
-        // check if there's a URI cache in the options.
-        URIResultCache* memCache = URIResultCache::from( localOptions );
-        if ( memCache )
-        {
-            URIResultCache::Record r = memCache->get( uri );
-            if ( r.valid() ) result = r.value();
-        }
+            READ_FUNCTOR reader;
 
-        if ( result.empty() )
-        {
-            // see if there's a read callback installed.
-            URIReadCallback* cb = Registry::instance()->getURIReadCallback();
+            URI uri = inputURI;
 
-            // for a local URI, bypass all the caching logic
-            if ( !uri.isRemote() )
-            {
-                // try to use the callback if it's set. Callback ignores the caching policy.
-                if ( cb )
-                {
-                    result = reader.fromCallback( cb, uri.full(), localOptions );
-                    if (result.code() != ReadResult::RESULT_NOT_IMPLEMENTED)
-                    {
-                        // only "NOT_IMPLEMENTED" is a reason to fallback. Anything else if a FAIL
-                        return result;
-                    }
-                }
+            bool gotResultFromCallback = false;
 
-                if ( result.empty() )
-                {
-                    result = reader.fromFile( uri.full(), localOptions );
-                }
+            // check if there's an alias map, and if so, attempt to resolve the alias:
+            URIAliasMap* aliasMap = URIAliasMap::from( localOptions );
+            if ( aliasMap )
+            {
+                uri = aliasMap->resolve(inputURI.full(), inputURI.context());
             }
 
-            // remote URI, consider caching:
-            else
+            // check if there's a URI cache in the options.
+            URIResultCache* memCache = URIResultCache::from( localOptions );
+            if ( memCache )
             {
-                bool callbackCachingOK = !cb || reader.callbackRequestsCaching(cb);
-
-                // by default, use a CachePolicy specified by the caller:
-                optional<CachePolicy> cp;
-                CachePolicy::fromOptions( localOptions, cp );
-
-                // if not, use the system defult:
-                if ( !cp.isSet() && Registry::instance()->defaultCachePolicy().isSet() )
-                    cp =  Registry::instance()->defaultCachePolicy().value();
-                
-                // otherwise, just use a default (read/write)
-                if ( !cp.isSet() )
-                    cp = CachePolicy::DEFAULT;
-
-
-                // get a cache bin if we need it:
-                CacheBin* bin = 0L;
-                if ( (cp->usage() != CachePolicy::USAGE_NO_CACHE) && callbackCachingOK )
+                URIResultCache::Record r = memCache->get( uri );
+                if ( r.valid() )
                 {
-                    bin = s_getCacheBin( dbOptions );
+                    result = r.value();
                 }
+            }
 
-                // first try to go to the cache if there is one:
-                if ( bin && cp->isCacheReadable() )
-                {
-                    result = reader.fromCache( bin, uri.cacheKey(), *cp->maxAge() );
-                    if ( result.succeeded() )
-                        result.setIsFromCache(true);
-                }
+            if ( result.empty() )
+            {
+                // see if there's a read callback installed.
+                URIReadCallback* cb = Registry::instance()->getURIReadCallback();
 
-                // not in the cache, so proceed to read it from the network.
-                if ( result.empty() )
+                // for a local URI, bypass all the caching logic
+                if ( !uri.isRemote() )
                 {
                     // try to use the callback if it's set. Callback ignores the caching policy.
                     if ( cb )
-                    {                
+                    {
+                        // if this returns "not implemented" we fill fall back
                         result = reader.fromCallback( cb, uri.full(), localOptions );
+
                         if ( result.code() != ReadResult::RESULT_NOT_IMPLEMENTED )
                         {
-                            return result;
+                            // "not implemented" is the only excuse to fall back.
+                            gotResultFromCallback = true;
                         }
                     }
 
-                    // still no data, go to the source:
-                    if ( result.empty() && cp->usage() != CachePolicy::USAGE_CACHE_ONLY )
+                    if ( !gotResultFromCallback )
                     {
-                        result = reader.fromHTTP( uri.full(), localOptions, progress );
+                        // no callback, just read from a local file.
+                        result = reader.fromFile( uri.full(), localOptions );
                     }
+                }
 
-                    // write the result to the cache if possible:
-                    if ( result.succeeded() && bin && cp->isCacheWriteable() )
+                // remote URI, consider caching:
+                else
+                {
+                    bool callbackCachingOK = !cb || reader.callbackRequestsCaching(cb);
+
+                    // establish the caching policy.
+                    optional<CachePolicy> cp;
+                    if ( !Registry::instance()->getCachePolicy( cp, localOptions ) )
+                        cp = CachePolicy::DEFAULT;
+
+                    // get a cache bin if we need it:
+                    CacheBin* bin = 0L;
+                    if ( (cp->usage() != CachePolicy::USAGE_NO_CACHE) && callbackCachingOK )
                     {
-                        bin->write( uri.cacheKey(), result.getObject(), result.metadata() );
+                        bin = s_getCacheBin( dbOptions );
                     }
-                }
 
-                OE_TEST << LC 
-                    << uri.base() << ": " 
-                    << (result.succeeded() ? "OK" : "FAILED") 
-                    << "; policy=" << cp->usageString()
-                    << (result.isFromCache() && result.succeeded() ? "; (from cache)" : "")
-                    << std::endl;
-            }
+                    // first try to go to the cache if there is one:
+                    if ( bin && cp->isCacheReadable() )
+                    {
+                        result = reader.fromCache( bin, uri.cacheKey(), *cp->maxAge() );
+                        if ( result.succeeded() )
+                            result.setIsFromCache(true);
+                    }
 
-            if ( result.getObject() )
-            {
-                result.getObject()->setName( uri.base() );
+                    // not in the cache, so proceed to read it from the network.
+                    if ( result.empty() )
+                    {
+                        // try to use the callback if it's set. Callback ignores the caching policy.
+                        if ( cb )
+                        {                
+                            result = reader.fromCallback( cb, uri.full(), localOptions );
+
+                            if ( result.code() != ReadResult::RESULT_NOT_IMPLEMENTED )
+                            {
+                                // "not implemented" is the only excuse for falling back
+                                gotResultFromCallback = true;
+                            }
+                        }
 
-                if ( memCache )
+                        if ( !gotResultFromCallback )
+                        {
+                            // still no data, go to the source:
+                            if ( result.empty() && cp->usage() != CachePolicy::USAGE_CACHE_ONLY )
+                            {
+                                result = reader.fromHTTP( uri.full(), localOptions, progress );
+                            }
+
+                            // write the result to the cache if possible:
+                            if ( result.succeeded() && bin && cp->isCacheWriteable() )
+                            {
+                                bin->write( uri.cacheKey(), result.getObject(), result.metadata() );
+                            }
+                        }
+                    }
+
+                    OE_TEST << LC 
+                        << uri.base() << ": " 
+                        << (result.succeeded() ? "OK" : "FAILED") 
+                        << "; policy=" << cp->usageString()
+                        << (result.isFromCache() && result.succeeded() ? "; (from cache)" : "")
+                        << std::endl;
+                }
+
+                    
+                if ( result.getObject() && !gotResultFromCallback )
                 {
-                    memCache->insert( uri, result );
+                    result.getObject()->setName( uri.base() );
+
+                    if ( memCache )
+                    {
+                        memCache->insert( uri, result );
+                    }
                 }
             }
         }
 
+        // post-process if there's a post-URI callback.
+        URIPostReadCallback* post = URIPostReadCallback::from(dbOptions);
+        if ( post )
+        {
+            (*post)(result);
+        }
+
         return result;
     }
 }
diff --git a/src/osgEarth/Version b/src/osgEarth/Version
index 991ba10..b578931 100644
--- a/src/osgEarth/Version
+++ b/src/osgEarth/Version
@@ -25,9 +25,10 @@
 extern "C" {
 
 #define OSGEARTH_MAJOR_VERSION    2
-#define OSGEARTH_MINOR_VERSION    2
+#define OSGEARTH_MINOR_VERSION    3
 #define OSGEARTH_PATCH_VERSION    0
 #define OSGEARTH_SOVERSION        0
+#define OSGEARTH_RC_VERSION       0
 
 /* Convenience macro that can be used to decide whether a feature is present or not i.e.
  * #if OSGEARTH_MIN_VERSION_REQUIRED(1,4,0)
diff --git a/src/osgEarth/Version.cpp b/src/osgEarth/Version.cpp
index 09caa85..344d773 100644
--- a/src/osgEarth/Version.cpp
+++ b/src/osgEarth/Version.cpp
@@ -30,13 +30,13 @@ const char* osgEarthGetVersion()
     static int osgearth_version_init = 1;
     if (osgearth_version_init)
     {
-        if (OSGEARTH_VERSION_REVISION==0)
+        if (OSGEARTH_RC_VERSION == 0 )
         {
             sprintf(osgearth_version,"%d.%d.%d",OSGEARTH_MAJOR_VERSION,OSGEARTH_MINOR_VERSION,OSGEARTH_PATCH_VERSION);
         }
         else
         {
-            sprintf(osgearth_version,"%d.%d.%d-%d",OSGEARTH_MAJOR_VERSION,OSGEARTH_MINOR_VERSION,OSGEARTH_PATCH_VERSION,OSGEARTH_VERSION_REVISION);
+            sprintf(osgearth_version,"%d.%d.%d RC%d",OSGEARTH_MAJOR_VERSION,OSGEARTH_MINOR_VERSION,OSGEARTH_PATCH_VERSION, OSGEARTH_RC_VERSION);
         }
 
         osgearth_version_init = 0;
diff --git a/src/osgEarth/ShaderComposition b/src/osgEarth/VirtualProgram
similarity index 70%
rename from src/osgEarth/ShaderComposition
rename to src/osgEarth/VirtualProgram
index ed61de4..5561189 100644
--- a/src/osgEarth/ShaderComposition
+++ b/src/osgEarth/VirtualProgram
@@ -16,8 +16,8 @@
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
-#ifndef OSGEARTH_SHADER_COMPOSITION_H
-#define OSGEARTH_SHADER_COMPOSITION_H 1
+#ifndef OSGEARTH_VIRTUAL_PROGRAM_H
+#define OSGEARTH_VIRTUAL_PROGRAM_H 1
 
 #include <osgEarth/Common>
 #include <osgEarth/Revisioning>
@@ -36,7 +36,7 @@
 #endif
 
 namespace osgEarth
-{        
+{
     namespace ShaderComp
     {
         // User function injection points.
@@ -55,85 +55,8 @@ namespace osgEarth
 
         // user function sets, categorized by function location.
         typedef std::map<FunctionLocation, OrderedFunctionMap> FunctionLocationMap;
-
-        // hints that can be used to automatically generate shader code
-        class OSGEARTH_EXPORT RenderingHints
-        {
-        public:
-            RenderingHints();
-            RenderingHints(const RenderingHints& rhs);
-            bool operator == (const RenderingHints& rhs) const;
-            bool operator != (const RenderingHints& rhs) const { return !operator==(rhs); }
-
-            void useNumTextures(unsigned num);
-            unsigned numTextures() const { return _numTextures; }
-
-        protected:
-            virtual ~RenderingHints() { }
-            unsigned _numTextures;
-        };
     }
 
-    /**
-     * A factory class that generates shader functions for the osgEarth engine.
-     * The default ShaderFactory is stored in the osgEarth registry. You can replace it
-     * if you want to replace osgEarth's default shader templates.
-     */
-    class OSGEARTH_EXPORT ShaderFactory : public osg::Referenced
-    {
-    public:
-        /** Creates a vertex shader main(). */
-        virtual osg::Shader* createVertexShaderMain(
-            const ShaderComp::FunctionLocationMap& functions =ShaderComp::FunctionLocationMap(),
-            bool useLightingShaders =true ) const;
-
-        /** Creates a fragment shader main(). */
-        virtual osg::Shader* createFragmentShaderMain( 
-            const ShaderComp::FunctionLocationMap& functions =ShaderComp::FunctionLocationMap(),
-            bool useLightingShaders =true ) const;
-
-        /**
-         * Gets the uniform/shader name of the sampler corresponding the the provider
-         * texture image unit
-         */
-        virtual std::string getSamplerName( unsigned texImageUnit ) const;
-        
-        /**
-         * Creates the function that sets up default texcoords in the vertex shader.
-         * The name/prototype is:
-         *    void osgearth_vert_setupColoring(); 
-         */
-        virtual osg::Shader* createDefaultColoringVertexShader( unsigned numTexCoordSets ) const;
-
-        /**
-         * Creates the function that applies texture data in the fragment shader.
-         * The name/prototype is:
-         *    osgearth_frag_applyColoring( inout vec4 color );
-         */
-        virtual osg::Shader* createDefaultColoringFragmentShader( unsigned numTexCoordSets ) const;
-
-        /**
-         * Creates the function that applies lighting calculations in the vertex shader.
-         * The name/prototype is:
-         *    void osgearth_vert_setupLighting();
-         */
-        virtual osg::Shader* createDefaultLightingVertexShader() const;
-
-        /**
-         * Creates the function that applies lighting coloring in the fragment shader.
-         * The name/prototype is:
-         *    void osgearth_frag_applyLighting( inout vec4 color ); 
-         */
-        virtual osg::Shader* createDefaultLightingFragmentShader() const;
-
-        /**
-         * Builds a shader that executes an image filter chain.
-         */
-        virtual osg::Shader* createColorFilterChainFragmentShader( const std::string& function, const ColorFilterChain& chain ) const;
-
-        /** dtor */
-        virtual ~ShaderFactory() { }
-    };
 
     /**
      * VirtualProgram enables basic GLSL shader composition within osgEarth.
@@ -174,17 +97,23 @@ namespace osgEarth
          * Installs default shaders for implementing basic coloring and lighting.
          * The default shaders come from the ShaderFactory.
          */
-        void installDefaultColoringAndLightingShaders( unsigned numTextures =0u );
+        void installDefaultColoringAndLightingShaders(
+            unsigned                           numTextures =0u,
+            osg::StateAttribute::OverrideValue qualifiers  =osg::StateAttribute::ON );
 
         /**
          * Installs default shader for basic coloring/texturing.
          */
-        void installDefaultColoringShaders( unsigned numTextures =0u );
+        void installDefaultColoringShaders( 
+            unsigned                           numTextures =0u,
+            osg::StateAttribute::OverrideValue qualifiers  =osg::StateAttribute::ON );
+
 
         /**
          * Installs default shader for basic lighting.
          */
-        void installDefaultLightingShaders();
+        void installDefaultLightingShaders(
+            osg::StateAttribute::OverrideValue qualifiers  =osg::StateAttribute::ON );
 
         /**
          * Sets whether to use lighting shaders at all - set this to false if you
@@ -301,5 +230,4 @@ namespace osgEarth
 
 } // namespace osgEarth
 
-#endif // OSGEARTH_SHADER_COMPOSITION_H
-
+#endif // OSGEARTH_VIRTUAL_PROGRAM_H
diff --git a/src/osgEarth/ShaderComposition.cpp b/src/osgEarth/VirtualProgram.cpp
similarity index 54%
rename from src/osgEarth/ShaderComposition.cpp
rename to src/osgEarth/VirtualProgram.cpp
index a058e7d..ad549fc 100644
--- a/src/osgEarth/ShaderComposition.cpp
+++ b/src/osgEarth/VirtualProgram.cpp
@@ -16,10 +16,12 @@
  * You should have received a copy of the GNU Lesser General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
+#include <osgEarth/ShaderFactory>
+#include <osgEarth/ShaderUtils>
 #include <osg/Shader>
 #include <osg/Program>
 #include <osg/State>
@@ -34,27 +36,24 @@ using namespace osgEarth::ShaderComp;
 #define OE_TEST OE_NULL
 //#define OE_TEST OE_NOTICE
 
-#ifdef OSG_GLES2_AVAILABLE
-    #define MERGE_SHADERS 1
-#else
-    //#define MERGE_SHADERS 1
-#endif
-
 //------------------------------------------------------------------------
 
 #define VERTEX_MAIN             "osgearth_vert_main"
 #define FRAGMENT_MAIN           "osgearth_frag_main"
 
-#define VERTEX_SETUP_COLORING   "osgearth_vert_setupColoring"
-#define VERTEX_SETUP_LIGHTING   "osgearth_vert_setupLighting"
-#define FRAGMENT_APPLY_COLORING "osgearth_frag_applyColoring"
-#define FRAGMENT_APPLY_LIGHTING "osgearth_frag_applyLighting"
-
-#define OSGEARTH_DUMP_SHADERS "OSGEARTH_DUMP_SHADERS"
+// environment variable control
+#define OSGEARTH_DUMP_SHADERS  "OSGEARTH_DUMP_SHADERS"
+#define OSGEARTH_MERGE_SHADERS "OSGEARTH_MERGE_SHADERS"
 
 namespace
 {
-    bool s_dumpShaders = false;
+#ifdef OSG_GLES2_AVAILABLE
+    bool s_mergeShaders = true;
+#else
+    bool s_mergeShaders = false;
+#endif
+
+    bool s_dumpShaders = false;        // debugging
 
     /** A hack for OSG 2.8.x to get access to the state attribute vector. */
     /** TODO: no longer needed in OSG 3+ ?? */
@@ -80,32 +79,6 @@ namespace
 
 //------------------------------------------------------------------------
 
-RenderingHints::RenderingHints() :
-_numTextures( 0 )
-{
-    //nop
-}
-
-RenderingHints::RenderingHints(const RenderingHints& rhs) :
-_numTextures( rhs._numTextures )
-{
-    //nop
-}
-
-bool
-RenderingHints::operator == (const RenderingHints& rhs) const
-{
-    return _numTextures == rhs._numTextures;
-}
-
-void
-RenderingHints::useNumTextures(unsigned num)
-{
-    _numTextures = std::max( _numTextures, num );
-}
-
-//------------------------------------------------------------------------
-
 // same type as PROGRAM (for proper state sorting)
 const osg::StateAttribute::Type VirtualProgram::SA_TYPE = osg::StateAttribute::PROGRAM;
 
@@ -116,7 +89,8 @@ _inherit           ( true ),
 _useLightingShaders( true )
 {
     // because we sometimes update/change the attribute's members from within the apply() method
-    this->setDataVariance( osg::Object::DYNAMIC );
+    // gw-commented out b/c apply() is only called from draw, and the changes are mutexed anyway
+    //this->setDataVariance( osg::Object::DYNAMIC );
 
     // check the the dump env var
     if ( ::getenv(OSGEARTH_DUMP_SHADERS) != 0L )
@@ -124,6 +98,12 @@ _useLightingShaders( true )
         s_dumpShaders = true;
     }
 
+    // check the merge env var
+    if ( ::getenv(OSGEARTH_MERGE_SHADERS) != 0L )
+    {
+        s_mergeShaders = true;
+    }
+
     // a template object to hold program data (so we don't have to dupliate all the 
     // osg::Program methods..)
     _template = new osg::Program();
@@ -132,12 +112,12 @@ _useLightingShaders( true )
 
 VirtualProgram::VirtualProgram(const VirtualProgram& rhs, const osg::CopyOp& copyop ) :
 osg::StateAttribute( rhs, copyop ),
-//osg::Program( rhs, copyop ),
 _shaderMap         ( rhs._shaderMap ),
 _mask              ( rhs._mask ),
 _functions         ( rhs._functions ),
 _inherit           ( rhs._inherit ),
-_useLightingShaders( rhs._useLightingShaders )
+_useLightingShaders( rhs._useLightingShaders ),
+_template          ( osg::clone(rhs._template.get()) )
 {
     //nop
 }
@@ -234,6 +214,10 @@ VirtualProgram::setShader(osg::Shader*                       shader,
         return 0L;
     }
 
+    // pre-processes the shader's source to include GLES uniforms as necessary
+    // (no-op on non-GLES)
+    ShaderPreProcessor::run( shader );
+
     _shaderMap[shader->getName()] = ShaderEntry(shader, ov);
 
     return shader;
@@ -329,39 +313,41 @@ VirtualProgram::addToAccumulatedMap(ShaderMap&         accumShaderMap,
 
 
 void
-VirtualProgram::installDefaultColoringAndLightingShaders( unsigned numTextures )
+VirtualProgram::installDefaultColoringAndLightingShaders(unsigned                           numTextures,
+                                                         osg::StateAttribute::OverrideValue qual )
 {
     ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
 
-    this->setShader( sf->createDefaultColoringVertexShader(numTextures) );
-    this->setShader( sf->createDefaultLightingVertexShader() );
+    this->setShader( sf->createDefaultColoringVertexShader(numTextures), qual );
+    this->setShader( sf->createDefaultLightingVertexShader(), qual );
 
-    this->setShader( sf->createDefaultColoringFragmentShader(numTextures) );
-    this->setShader( sf->createDefaultLightingFragmentShader() );
+    this->setShader( sf->createDefaultColoringFragmentShader(numTextures), qual );
+    this->setShader( sf->createDefaultLightingFragmentShader(), qual );
 
     setUseLightingShaders( true );
 }
 
 
 void
-VirtualProgram::installDefaultLightingShaders()
+VirtualProgram::installDefaultLightingShaders(osg::StateAttribute::OverrideValue qual)
 {
     ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
 
-    this->setShader( sf->createDefaultLightingVertexShader() );
-    this->setShader( sf->createDefaultLightingFragmentShader() );
+    this->setShader( sf->createDefaultLightingVertexShader(), qual );
+    this->setShader( sf->createDefaultLightingFragmentShader(), qual );
 
     setUseLightingShaders( true );
 }
 
 
 void
-VirtualProgram::installDefaultColoringShaders( unsigned numTextures )
+VirtualProgram::installDefaultColoringShaders(unsigned                           numTextures,
+                                              osg::StateAttribute::OverrideValue qual )
 {
     ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
 
-    this->setShader( sf->createDefaultColoringVertexShader(numTextures) );
-    this->setShader( sf->createDefaultColoringFragmentShader(numTextures) );
+    this->setShader( sf->createDefaultColoringVertexShader(numTextures), qual );
+    this->setShader( sf->createDefaultColoringFragmentShader(numTextures), qual );
 }
 
 
@@ -399,12 +385,14 @@ namespace
         StringVector lines;
         StringTokenizer( source, lines, "\n", "", true, false );
 
-        for( StringVector::const_iterator line = lines.begin(); line != lines.end(); ++line )
+        for( StringVector::const_iterator line_iter = lines.begin(); line_iter != lines.end(); ++line_iter )
         {
-            if ( line->size() > 0 )
+            std::string line = trim(*line_iter);
+
+            if ( line.size() > 0 )
             {
                 StringVector tokens;
-                StringTokenizer( *line, tokens, " \t", "", false, true );
+                StringTokenizer( line, tokens, " \t", "", false, true );
 
                 if (tokens[0] == "#version")
                 {
@@ -427,13 +415,13 @@ namespace
                     tokens[0] == "uniform"      ||
                     tokens[0] == "attribute")
                 {
-                    std::string& header = headers[*line];
-                    header = *line;
+                    std::string& header = headers[line];
+                    header = line;
                 }
 
                 else
                 {
-                    body << (*line) << "\n";
+                    body << (*line_iter) << "\n";
                 }
             }
         }
@@ -446,13 +434,7 @@ VirtualProgram::addShadersToProgram(const ShaderVector&      shaders,
                                     const AttribBindingList& attribBindings,
                                     osg::Program*            program )
 {
-#ifdef MERGE_SHADERS
-    bool mergeShaders = true;
-#else
-    bool mergeShaders = false;
-#endif
-
-    if ( mergeShaders )
+    if ( s_mergeShaders )
     {
         unsigned          vertVersion = 0;
         HeaderMap         vertHeaders;
@@ -501,9 +483,12 @@ VirtualProgram::addShadersToProgram(const ShaderVector&      shaders,
         program->addShader( new osg::Shader(osg::Shader::VERTEX, vertBodyText) );
         program->addShader( new osg::Shader(osg::Shader::FRAGMENT, fragBodyText) );
 
-        OE_TEST << LC 
-            << "\nMERGED VERTEX SHADER: \n\n" << vertBodyText << "\n\n"
-            << "MERGED FRAGMENT SHADER: \n\n" << fragBodyText << "\n" << std::endl;
+        if ( s_dumpShaders )
+        {
+            OE_NOTICE << LC 
+                << "\nMERGED VERTEX SHADER: \n\n" << vertBodyText << "\n\n"
+                << "MERGED FRAGMENT SHADER: \n\n" << fragBodyText << "\n" << std::endl;
+        }
     }
     else
     {
@@ -556,18 +541,6 @@ VirtualProgram::buildProgram(osg::State&        state,
     osg::ref_ptr<osg::Shader> oldFragMain = _fragMain.get();
     _fragMain = sf->createFragmentShaderMain( _accumulatedFunctions, _useLightingShaders );
 
-#if 0
-    osg::Shader* old_vert_main = getShader( VERTEX_MAIN );
-    osg::ref_ptr<osg::Shader> vert_main = sf->createVertexShaderMain( _accumulatedFunctions, _useLightingShaders );
-    setShader( VERTEX_MAIN, vert_main.get() );
-    addToAccumulatedMap( accumShaderMap, VERTEX_MAIN, ShaderEntry(vert_main.get(), osg::StateAttribute::ON) );
-
-    osg::Shader* old_frag_main = getShader( FRAGMENT_MAIN );
-    osg::ref_ptr<osg::Shader> frag_main = sf->createFragmentShaderMain( _accumulatedFunctions, _useLightingShaders );
-    setShader( FRAGMENT_MAIN, frag_main.get() );
-    addToAccumulatedMap( accumShaderMap, FRAGMENT_MAIN, ShaderEntry(frag_main.get(), osg::StateAttribute::ON) );
-#endif
-
     // rebuild the shader list now that we've changed the shader map.
     ShaderVector keyVector;
     for( ShaderMap::iterator i = accumShaderMap.begin(); i != accumShaderMap.end(); ++i )
@@ -594,7 +567,6 @@ VirtualProgram::buildProgram(osg::State&        state,
 
     // Since we replaced the "mains", we have to go through the cache and update all its
     // entries to point at the new mains instead of the old ones.
-//    if ( old_vert_main || old_frag_main )
     if ( oldVertMain.valid() || oldFragMain.valid() )
     {
         ProgramMap newProgramCache;
@@ -603,21 +575,6 @@ VirtualProgram::buildProgram(osg::State&        state,
         {
             const ShaderVector& originalKey = m->first;
 
-#if 0
-            // build a new cache key:
-            ShaderVector newKey;
-
-            for( ShaderVector::const_iterator i = original.begin(); i != original.end(); ++i )
-            {
-                if ( i->get() == old_vert_main )
-                    newKey.push_back( vert_main.get() );
-                else if ( i->get() == old_frag_main )
-                    newKey.push_back( frag_main.get() );
-                else
-                    newKey.push_back( i->get() );
-            }
-#endif
-
             osg::Program* newProgram = new osg::Program();
             newProgram->setName( m->second->getName() );
 
@@ -628,7 +585,6 @@ VirtualProgram::buildProgram(osg::State&        state,
 
             addTemplateDataToProgram( newProgram );
 
-            //newProgramCache[newKey] = newProgram;
             newProgramCache[originalKey] = newProgram;
         }
 
@@ -798,10 +754,22 @@ VirtualProgram::refreshAccumulatedFunctions( const osg::State& state )
 
                     for( FunctionLocationMap::const_iterator j = rhs.begin(); j != rhs.end(); ++j )
                     {
-                        const OrderedFunctionMap& ofm = j->second;
-                        for( OrderedFunctionMap::const_iterator k = ofm.begin(); k != ofm.end(); ++k )
+                        const OrderedFunctionMap& source = j->second;
+                        OrderedFunctionMap&       dest   = _accumulatedFunctions[j->first];
+
+                        for( OrderedFunctionMap::const_iterator k = source.begin(); k != source.end(); ++k )
                         {
-                            _accumulatedFunctions[j->first].insert( *k );
+                            // remove/override an existing function with the same name
+                            for( OrderedFunctionMap::iterator exists = dest.begin(); exists != dest.end(); ++exists )
+                            {
+                                if ( exists->second.compare( k->second ) == 0 )
+                                {
+                                    dest.erase(exists);
+                                    break;
+                                }
+                            }
+                            dest.insert( *k );
+                            //_accumulatedFunctions[j->first].insert( *k );
                         }
                     }
                 }
@@ -812,461 +780,22 @@ VirtualProgram::refreshAccumulatedFunctions( const osg::State& state )
     // add the local ones too:
     for( FunctionLocationMap::const_iterator j = _functions.begin(); j != _functions.end(); ++j )
     {
-        const OrderedFunctionMap& ofm = j->second;
-        for( OrderedFunctionMap::const_iterator k = ofm.begin(); k != ofm.end(); ++k )
-        {
-            _accumulatedFunctions[j->first].insert( *k );
-        }
-    } 
-}
-
-//----------------------------------------------------------------------------
-
-std::string
-ShaderFactory::getSamplerName( unsigned unit ) const
-{
-    return Stringify() << "osgearth_tex" << unit;
-}
-
-
-osg::Shader*
-ShaderFactory::createVertexShaderMain(const FunctionLocationMap& functions,
-                                      bool  useLightingShaders ) const
-{
-    FunctionLocationMap::const_iterator i = functions.find( LOCATION_VERTEX_PRE_TEXTURING );
-    const OrderedFunctionMap* preTexture = i != functions.end() ? &i->second : 0L;
-
-    FunctionLocationMap::const_iterator j = functions.find( LOCATION_VERTEX_PRE_LIGHTING );
-    const OrderedFunctionMap* preLighting = j != functions.end() ? &j->second : 0L;
-
-    FunctionLocationMap::const_iterator k = functions.find( LOCATION_VERTEX_POST_LIGHTING );
-    const OrderedFunctionMap* postLighting = k != functions.end() ? &k->second : 0L;
-
-    std::stringstream buf;
-    buf << "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-        << "precision mediump float;\n"
-#endif
-        << "void osgearth_vert_setupColoring(); \n";
-
-    if ( useLightingShaders )
-        buf << "void osgearth_vert_setupLighting(); \n";
-
-    if ( preTexture )
-        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
-            buf << "void " << i->second << "(); \n";
-
-    if ( preLighting )
-        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
-            buf << "void " << i->second << "(); \n";
-
-    if ( postLighting )
-        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
-            buf << "void " << i->second << "(); \n";
-
-    buf << "void main(void) \n"
-        << "{ \n"
-        << "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n";
-
-    if ( preTexture )
-        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
-            buf << "    " << i->second << "(); \n";
-
-    buf << "    osgearth_vert_setupColoring(); \n";
-    
-    if ( preLighting )
-        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
-            buf << "    " << i->second << "(); \n";
-
-    if ( useLightingShaders )
-        buf << "    osgearth_vert_setupLighting(); \n";
-    
-    if ( postLighting )
-        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
-            buf << "    " << i->second << "(); \n";
-
-    buf << "} \n";
-
-    std::string str;
-    str = buf.str();
-    //OE_INFO << str << std::endl;
-    return new osg::Shader( osg::Shader::VERTEX, str );
-}
-
-
-osg::Shader*
-ShaderFactory::createFragmentShaderMain(const FunctionLocationMap& functions,
-                                        bool  useLightingShaders ) const
-{
-    FunctionLocationMap::const_iterator i = functions.find( LOCATION_FRAGMENT_PRE_TEXTURING );
-    const OrderedFunctionMap* preTexture = i != functions.end() ? &i->second : 0L;
-
-    FunctionLocationMap::const_iterator j = functions.find( LOCATION_FRAGMENT_PRE_LIGHTING );
-    const OrderedFunctionMap* preLighting = j != functions.end() ? &j->second : 0L;
-
-    FunctionLocationMap::const_iterator k = functions.find( LOCATION_FRAGMENT_POST_LIGHTING );
-    const OrderedFunctionMap* postLighting = k != functions.end() ? &k->second : 0L;
-
-    std::stringstream buf;
-    buf << "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-        << "precision mediump float;\n"
-#endif
-        << "void osgearth_frag_applyColoring( inout vec4 color ); \n";
-
-    if ( useLightingShaders )
-        buf << "void osgearth_frag_applyLighting( inout vec4 color ); \n";
-
-    if ( preTexture )
-        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
-            buf << "void " << i->second << "( inout vec4 color ); \n";
-
-    if ( preLighting )
-        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
-            buf << "void " << i->second << "( inout vec4 color ); \n";
-
-    if ( postLighting )
-        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
-            buf << "void " << i->second << "( inout vec4 color ); \n";
-
-    buf << "void main(void) \n"
-        << "{ \n"
-        << "    vec4 color = vec4(1,1,1,1); \n"; //gl_Color; \n"; //vec4(1,1,1,1); \n";
-
-    if ( preTexture )
-        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
-            buf << "    " << i->second << "( color ); \n";
-
-    buf << "    osgearth_frag_applyColoring( color ); \n";
-
-    if ( preLighting )
-        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
-            buf << "    " << i->second << "( color ); \n";
-    
-    if ( useLightingShaders )
-        buf << "    osgearth_frag_applyLighting( color ); \n";
-
-    if ( postLighting )
-        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
-            buf << "    " << i->second << "( color ); \n";
-
-    buf << "    gl_FragColor = color; \n"
-
-#if 0 // GW: testing logarithmic depth buffer remapping
-        << "    float A = gl_ProjectionMatrix[2].z; \n"
-        << "    float B = gl_ProjectionMatrix[3].z; \n"
-        << "    float n = -B/(1.0-A); \n"
-        << "    float f =  B/(1.0+A); \n"
-        << "    float C = 1; \n"
-        << "    gl_FragDepth = log(C*gl_FragCoord.z+1) / log(C*f+1); \n"
-#endif
-        << "} \n";  
-
-    std::string str;
-    str = buf.str();
-    //OE_INFO << str;
-    return new osg::Shader( osg::Shader::FRAGMENT, str );
-}
- 
-
-osg::Shader*
-ShaderFactory::createDefaultColoringVertexShader( unsigned numTexCoordSets ) const
-{
-    std::stringstream buf;
-
-    buf << "#version " << GLSL_VERSION_STR << "\n";
-#ifdef OSG_GLES2_AVAILABLE
-    buf << "precision mediump float;\n";
-#endif
-    
-    //if ( numTexCoordSets > 0 )
-    //{
-    //    buf << "varying vec4 osg_TexCoord[" << numTexCoordSets << "];\n";
-    //}
-    buf << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "];\n";
-
-    buf
-        << "varying vec4 osg_FrontColor;\n"
-        << "varying vec4 osg_FrontSecondaryColor;\n"
-    
-        << "void osgearth_vert_setupColoring() \n"
-        << "{ \n"
-        << "    osg_FrontColor = gl_Color; \n"
-        << "    osg_FrontSecondaryColor = vec4(0.0); \n";
-
-    //TODO: gl_TexCoord et.al. are depcrecated so we should replace them;
-    // this approach also only support up to 8 texture coord units
-    for(unsigned i=0; i<numTexCoordSets; ++i )
-    {
-        buf << "    osg_TexCoord["<< i <<"] = gl_MultiTexCoord"<< i << "; \n";
-    }
-        
-    buf << "} \n";
-
-    std::string str;
-    str = buf.str();
-
-    osg::Shader* shader = new osg::Shader(osg::Shader::VERTEX, str);
-    shader->setName( VERTEX_SETUP_COLORING );
-    return shader;
-}
-
-
-osg::Shader*
-ShaderFactory::createDefaultColoringFragmentShader( unsigned numTexImageUnits ) const
-{
-    std::stringstream buf;
-
-    buf << "#version " << GLSL_VERSION_STR << "\n";
-#ifdef OSG_GLES2_AVAILABLE
-    buf << "precision mediump float;\n";
-#endif
-    
-    buf << "varying vec4 osg_FrontColor;\n";
-    
-    if ( numTexImageUnits > 0 )
-    {
-        buf << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "];\n";
-        buf << "uniform sampler2D ";
-        for( unsigned i=0; i<numTexImageUnits; ++i )
-        {
-            buf << getSamplerName(i) << (i+1 < numTexImageUnits? "," : "; \n");
-        }
-    }
-
-    buf << "void osgearth_frag_applyColoring( inout vec4 color ) \n"
-        << "{ \n"
-        << "    color = color * osg_FrontColor; \n";
-    
-    if ( numTexImageUnits > 0 )
-    {
-        buf << "    vec4 texel; \n";
+        const OrderedFunctionMap& source = j->second;
+        OrderedFunctionMap&       dest   = _accumulatedFunctions[j->first];
 
-        for(unsigned i=0; i<numTexImageUnits; ++i )
+        for( OrderedFunctionMap::const_iterator k = source.begin(); k != source.end(); ++k )
         {
-            buf << "    texel = texture2D(" << getSamplerName(i) << ", osg_TexCoord["<< i <<"].st); \n";
-            buf << "    color.rgb = mix( color.rgb, texel.rgb, texel.a ); \n";
-            if ( i == 0 )
-                buf << "    color.a = texel.a * color.a; \n";
+            // remove/override an existing function with the same name
+            for( OrderedFunctionMap::iterator exists = dest.begin(); exists != dest.end(); ++exists )
+            {
+                if ( exists->second.compare( k->second ) == 0 )
+                {
+                    dest.erase(exists);
+                    break;
+                }
+            }
+            dest.insert( *k );
+            //_accumulatedFunctions[j->first].insert( *k );
         }
-    }
-
-    buf << "} \n";
-
-    std::string str;
-    str = buf.str();
-
-    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, str );
-    shader->setName( FRAGMENT_APPLY_COLORING );
-    return shader;
-}
-
-
-osg::Shader*
-ShaderFactory::createDefaultLightingVertexShader() const
-{
-    int maxLights = Registry::capabilities().getMaxLights();
-    
-    std::stringstream buf;
-    buf << "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-    << "precision mediump float;\n"
-    
-    //add lightsource typedef and uniform array
-    << "struct osg_LightSourceParameters {"
-    << "    vec4  ambient;"
-    << "    vec4  diffuse;"
-    << "    vec4  specular;"
-    << "    vec4  position;"
-    << "    vec4  halfVector;"
-    << "    vec3  spotDirection;" 
-    << "    float  spotExponent;"
-    << "    float  spotCutoff;"
-    << "    float  spotCosCutoff;" 
-    << "    float  constantAttenuation;"
-    << "    float  linearAttenuation;"
-    << "    float  quadraticAttenuation;" 
-    << "};\n"
-    << "uniform osg_LightSourceParameters osg_LightSource[" << maxLights << "];\n"
-    
-    << "struct  osg_LightProducts {"
-    << "    vec4  ambient;"
-    << "    vec4  diffuse;"
-    << "    vec4  specular;"
-    << "};\n"
-    << "uniform osg_LightProducts osg_FrontLightProduct[" << maxLights << "];\n"
-    
-#endif
-    
-    << "varying vec4 osg_FrontColor;\n"
-    << "varying vec4 osg_FrontSecondaryColor;\n"
-    
-    << "uniform bool osgearth_LightingEnabled; \n"
-    
-#ifndef OSG_GLES2_AVAILABLE
-    << "void osgearth_vert_setupLighting() \n"
-    << "{ \n"
-    << "    if (osgearth_LightingEnabled) \n"
-    << "    { \n"
-    << "        vec3 normal = gl_NormalMatrix * gl_Normal; \n"
-    << "        float NdotL = dot( normal, normalize(gl_LightSource[0].position.xyz) ); \n"
-    << "        NdotL = max( 0.0, NdotL ); \n"
-    << "        float NdotHV = dot( normal, gl_LightSource[0].halfVector.xyz ); \n"
-    << "        NdotHV = max( 0.0, NdotHV ); \n"
-
-    << "        osg_FrontColor.rgb = osg_FrontColor.rgb * \n"
-    << "            clamp( \n"
-    << "                gl_LightModel.ambient + \n"
-    << "                gl_FrontLightProduct[0].ambient +          \n"
-    << "                gl_FrontLightProduct[0].diffuse * NdotL, 0.0, 1.0).rgb;   \n"
-
-    << "        osg_FrontSecondaryColor = vec4(0.0); \n"
-    << "        if ( NdotL * NdotHV > 0.0 ) \n"
-    << "        { \n"
-    << "            osg_FrontSecondaryColor.rgb = (gl_FrontLightProduct[0].specular * \n"
-    << "                                          pow( NdotHV, gl_FrontMaterial.shininess )).rgb;\n"
-    << "        } \n"
-
-//    << "        gl_BackColor = gl_FrontColor; \n"
-//    << "        gl_BackSecondaryColor = gl_FrontSecondaryColor; \n"
-    << "    } \n"
-    << "} \n";
-#else
-    << "void osgearth_vert_setupLighting() \n"
-    << "{ \n"
-    << "    if (osgearth_LightingEnabled) \n"
-    << "    { \n"
-    << "        float shine = 10.0;\n"
-    << "        vec4 lightModelAmbi = vec4(0.1,0.1,0.1,1.0);\n"
-//gl_FrontMaterial.shininess
-//gl_LightModel.ambient
-    << "        vec3 normal = gl_NormalMatrix * gl_Normal; \n"
-    << "        float NdotL = dot( normal, normalize(osg_LightSource[0].position.xyz) ); \n"
-    << "        NdotL = max( 0.0, NdotL ); \n"
-    << "        float NdotHV = dot( normal, osg_LightSource[0].halfVector.xyz ); \n"
-    << "        NdotHV = max( 0.0, NdotHV ); \n"
-    
-    << "        osg_FrontColor.rgb = osg_FrontColor.rgb * \n"
-    << "            clamp( \n"
-    << "                lightModelAmbi + \n"
-    << "                osg_FrontLightProduct[0].ambient +          \n"
-    << "                osg_FrontLightProduct[0].diffuse * NdotL, 0.0, 1.0).rgb;   \n"
-    
-    << "        osg_FrontSecondaryColor = vec4(0.0); \n"
-    
-    << "        if ( NdotL * NdotHV > 0.0 ) \n"
-    << "        { \n"
-    << "            osg_FrontSecondaryColor.rgb = (osg_FrontLightProduct[0].specular * \n"
-    << "                                          pow( NdotHV, shine )).rgb;\n"
-    << "        } \n"
-    //    << "        gl_BackColor = gl_FrontColor; \n"
-    //    << "        gl_BackSecondaryColor = gl_FrontSecondaryColor; \n"
-    << "    } \n"
-    << "} \n";
-#endif
-
-    osg::Shader* shader = new osg::Shader( osg::Shader::VERTEX, buf.str().c_str() );
-    shader->setName( VERTEX_SETUP_LIGHTING );
-    return shader;
-}
-
-
-osg::Shader*
-ShaderFactory::createDefaultLightingFragmentShader() const
-{
-    std::stringstream buf;
-    
-    buf << "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-    << "precision mediump float;\n"
-#endif
-    
-    << "varying vec4 osg_FrontColor;\n"
-    << "varying vec4 osg_FrontSecondaryColor;\n"
-    
-    << "uniform bool osgearth_LightingEnabled; \n"
-    << "void osgearth_frag_applyLighting( inout vec4 color ) \n"
-    << "{ \n"
-    << "    if ( osgearth_LightingEnabled ) \n"
-    << "    { \n"
-    << "        float alpha = color.a; \n"
-    << "        color = (color * osg_FrontColor) + osg_FrontSecondaryColor; \n"
-    << "        color.a = alpha; \n"
-    << "    } \n"
-    << "} \n";
-
-    osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, buf.str().c_str() );
-    shader->setName( FRAGMENT_APPLY_LIGHTING );
-    return shader;
-}
-
-
-osg::Shader*
-ShaderFactory::createColorFilterChainFragmentShader( const std::string& function, const ColorFilterChain& chain ) const
-{
-    std::stringstream buf;
-    buf << "#version " << GLSL_VERSION_STR << "\n";
-#ifdef OSG_GLES2_AVAILABLE
-    buf << "precision mediump float;\n";
-#endif
-
-    // write out the shader function prototypes:
-    for( ColorFilterChain::const_iterator i = chain.begin(); i != chain.end(); ++i )
-    {
-        ColorFilter* filter = i->get();
-        buf << "void " << filter->getEntryPointFunctionName() << "(in int slot, inout vec4 color);\n";
-    }
-
-    // write out the main function:
-    buf << "void " << function << "(in int slot, inout vec4 color) \n"
-        << "{ \n";
-
-    // write out the function calls. if there are none, it's a NOP.
-    for( ColorFilterChain::const_iterator i = chain.begin(); i != chain.end(); ++i )
-    {
-        ColorFilter* filter = i->get();
-        buf << "    " << filter->getEntryPointFunctionName() << "(slot, color);\n";
-    }
-        
-    buf << "} \n";
-
-    std::string bufstr;
-    bufstr = buf.str();
-    return new osg::Shader(osg::Shader::FRAGMENT, bufstr);
+    } 
 }
-
-
-//--------------------------------------------------------------------------
-
-#if 0
-// This is just a holding pen for various stuff
-
-static char s_PerFragmentLighting_VertexShaderSource[] =
-    "varying vec3 Normal; \n"
-    "varying vec3 Position; \n"
-    "void osgearth_vert_setupLighting() \n"
-    "{ \n"
-    "    Normal = normal; \n"
-    "    Position = position; \n"
-    "} \n";
-
-static char s_PerFragmentDirectionalLighting_FragmentShaderSource[] =
-    "varying vec3 Normal; \n"
-    "varying vec3 Position; \n"
-    "void osgearth_frag_applyLighting( inout vec4 color ) \n"
-    "{ \n"
-    "    vec3 n = normalize( Normal ); \n"
-    "    float NdotL = dot( n, normalize(gl_LightSource[0].position.xyz) ); \n"
-    "    NdotL = max( 0.0, NdotL ); \n"
-    "    float NdotHV = dot( n, gl_LightSource[0].halfVector.xyz ); \n"
-    "    NdotHV = max( 0.0, NdotHV ); \n"
-    "    color *= gl_FrontLightModelProduct.sceneColor + \n"
-    "             gl_FrontLightProduct[0].ambient + \n"
-    "             gl_FrontLightProduct[0].diffuse * NdotL; \n"
-    "    if ( NdotL * NdotHV > 0.0 ) \n"
-    "        color += gl_FrontLightProduct[0].specular * \n"
-    "                 pow( NdotHV, gl_FrontMaterial.shininess ); \n"
-    "} \n";
-
-#endif
-
diff --git a/src/osgEarth/XmlUtils.cpp b/src/osgEarth/XmlUtils.cpp
index 7dac714..77c9cc1 100644
--- a/src/osgEarth/XmlUtils.cpp
+++ b/src/osgEarth/XmlUtils.cpp
@@ -20,6 +20,7 @@
 #include <osgEarth/XmlUtils>
 #include <osgEarth/StringUtils>
 #include <osg/Notify>
+
 #include "tinyxml.h"
 #include <algorithm>
 #include <sstream>
@@ -429,49 +430,46 @@ XmlDocument::getConfig() const
     return conf;
 }
 
-#define INDENT 4
-
 static void
-storeNode( const XmlNode* node, int depth, std::ostream& out )
+storeNode( const XmlNode* node, TiXmlNode* parent)
 {
-    out << std::fixed; // always use fixed notation
-
-    for( int k=0; k<depth*INDENT; k++ ) out << " ";
-
-    if ( node->isElement() )
+    if (node->isElement())
     {
         XmlElement* e = (XmlElement*)node;
-        out << "<" << e->getName();
+        TiXmlElement* element = new TiXmlElement( e->getName().c_str() );
+        //Write out all the attributes
         for( XmlAttributes::iterator a = e->getAttrs().begin(); a != e->getAttrs().end(); a++ )
         {
-            out << " " << a->first << "=" << "\"" << a->second << "\"";
+            element->SetAttribute(a->first.c_str(), a->second.c_str() );            
         }
-        out << ">" << std::endl;
+
+        //Write out all the child nodes
         for( XmlNodeList::iterator i = e->getChildren().begin(); i != e->getChildren().end(); i++ )
         {
-            storeNode( i->get(), depth+1, out );
+            storeNode( i->get(), element );
         }
-        for( int k=0; k<depth*INDENT; k++ ) out << " ";
-        out << "</" << e->getName() << ">" << std::endl;
+        parent->LinkEndChild( element );
     }
-    else if ( node->isText() )
+    else if (node->isText())
     {
         XmlText* t = (XmlText*)node;
-        const std::string& text = t->getValue();
-        if ( text.find_first_of( "<&" ) != std::string::npos )
-            out << "<![CDATA[" << text << "]]>" << std::endl;
-        else
-            out << text << std::endl;
+        parent->LinkEndChild( new TiXmlText( t->getValue().c_str() ) );
     }
 }
 
 void
 XmlDocument::store( std::ostream& out ) const
-{
-    out << "<?xml version=\"1.0\"?>" << std::endl;
-    storeNode( this, 0, out);
-    /*for( XmlNodeList::const_iterator i = getChildren().begin(); i != getChildren().end(); i++ )
-    {
-        storeNode( i->get(), 0, out );
-    }*/
+{    
+    TiXmlDocument doc;
+    doc.LinkEndChild( new TiXmlDeclaration( "1.0", "", ""));
+    storeNode( this, &doc );    
+
+
+    //Use TiXmlPrinter to do pretty printing.
+    TiXmlPrinter printer;
+    printer.SetIndent("  ");
+    doc.Accept(&printer);
+    out << printer.CStr();
+
+    //out << doc;    
 }
diff --git a/src/osgEarthAnnotation/AnnotationNode b/src/osgEarthAnnotation/AnnotationNode
index ebeaea5..eee588b 100644
--- a/src/osgEarthAnnotation/AnnotationNode
+++ b/src/osgEarthAnnotation/AnnotationNode
@@ -151,12 +151,20 @@ namespace osgEarth { namespace Annotation
          */
         virtual void setDepthAdjustment( bool value );
 
+        /**
+         * Sets a lighting override but only if lighting is not already set.
+         */
+        void setLightingIfNotSet( bool lighting );
+
         // utility funcion to make a geopoint absolute height
         bool makeAbsolute( GeoPoint& mapPoint, osg::Node* patch =0L ) const;
 
         // hidden default ctor
         AnnotationNode( MapNode* mapNode =0L );
 
+        // hidden config ctor
+        AnnotationNode( MapNode* mapNode, const Config& conf );
+
         // hidden copy ctor
         AnnotationNode(const AnnotationNode& rhs, const osg::CopyOp& op=osg::CopyOp::DEEP_COPY_ALL) { }
 
@@ -194,7 +202,14 @@ namespace osgEarth { namespace Annotation
 
     protected:
 
-        PositionedAnnotationNode(MapNode* mapNode =0L) : AnnotationNode(mapNode) { }
+        PositionedAnnotationNode()
+            : AnnotationNode() { }
+
+        PositionedAnnotationNode(MapNode* mapNode)
+            : AnnotationNode( mapNode ) { }
+
+        PositionedAnnotationNode(MapNode* mapNode, const Config& conf)
+            : AnnotationNode( mapNode, conf ) { }
         
     };
 
diff --git a/src/osgEarthAnnotation/AnnotationNode.cpp b/src/osgEarthAnnotation/AnnotationNode.cpp
index 13fc4cb..6207e82 100644
--- a/src/osgEarthAnnotation/AnnotationNode.cpp
+++ b/src/osgEarthAnnotation/AnnotationNode.cpp
@@ -61,6 +61,35 @@ _activeDs   ( 0L )
     //nop
     //Note: Cannot call setMapNode() here because it's a virtual function.
     //      Each subclass will be separately responsible at ctor time.
+
+    // always blend.
+    this->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
+}
+
+AnnotationNode::AnnotationNode(MapNode* mapNode, const Config& conf) :
+_mapNode    ( mapNode ),
+_dynamic    ( false ),
+_autoclamp  ( false ),
+_depthAdj   ( false ),
+_activeDs   ( 0L )
+{
+    if ( conf.hasValue("lighting") )
+    {
+        bool lighting = conf.value<bool>("lighting", false);
+        setLightingIfNotSet( lighting );
+    }
+
+    if ( conf.hasValue("backface_culling") )
+    {
+        bool culling = conf.value<bool>("backface_culling", false);
+        getOrCreateStateSet()->setMode( GL_CULL_FACE, (culling?1:0) | osg::StateAttribute::OVERRIDE );
+    }
+
+    if ( conf.hasValue("blending") )
+    {
+        bool blending = conf.value<bool>("blending", false);
+        getOrCreateStateSet()->setMode( GL_BLEND, (blending?1:0) | osg::StateAttribute::OVERRIDE );
+    }
 }
 
 AnnotationNode::~AnnotationNode()
@@ -69,6 +98,19 @@ AnnotationNode::~AnnotationNode()
 }
 
 void
+AnnotationNode::setLightingIfNotSet( bool lighting )
+{
+    osg::StateSet* ss = this->getOrCreateStateSet();
+
+    if ( ss->getMode(GL_LIGHTING) == osg::StateAttribute::INHERIT )
+    {
+        this->getOrCreateStateSet()->setMode(
+            GL_LIGHTING,
+            (lighting ? 1 : 0) | osg::StateAttribute::OVERRIDE );
+    }
+}
+
+void
 AnnotationNode::setMapNode( MapNode* mapNode )
 {
     if ( getMapNode() != mapNode )
@@ -327,7 +369,4 @@ AnnotationNode::applyStyle( const Style& style)
         _altitude = style.get<AltitudeSymbol>();
         setAutoClamp( true );
     }
-
-    bool enableLighting = style.has<ExtrusionSymbol>();
-    this->getOrCreateStateSet()->setMode( GL_LIGHTING, enableLighting );
 }
diff --git a/src/osgEarthAnnotation/AnnotationUtils b/src/osgEarthAnnotation/AnnotationUtils
index 30d4262..6bb3b29 100644
--- a/src/osgEarthAnnotation/AnnotationUtils
+++ b/src/osgEarthAnnotation/AnnotationUtils
@@ -74,11 +74,15 @@ namespace osgEarth { namespace Annotation
          */
         static osg::Uniform* createHighlightUniform();
 
+#if 0
         /**
          * Install a program that implements fading, highligting, etc. on annotation 
          * objects.
          */
-        static void installAnnotationProgram( osg::StateSet* stateSet );
+        static void installAnnotationProgram(
+            osg::StateSet*                     stateSet,
+            osg::StateAttribute::OverrideValue value    =osg::StateAttribute::ON );
+#endif
 
         /**
          * Builds a graph on top of the specified that that implements a 2-pass
diff --git a/src/osgEarthAnnotation/AnnotationUtils.cpp b/src/osgEarthAnnotation/AnnotationUtils.cpp
index e3d0f7a..2d4052d 100644
--- a/src/osgEarthAnnotation/AnnotationUtils.cpp
+++ b/src/osgEarthAnnotation/AnnotationUtils.cpp
@@ -23,7 +23,7 @@
 #include <osgEarthSymbology/MeshSubdivider>
 #include <osgEarth/ThreadingUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/Capabilities>
 
 
@@ -124,6 +124,11 @@ AnnotationUtils::createTextDrawable(const std::string& text,
     {
         t->setBackdropColor( symbol->halo()->color() );
         t->setBackdropType( osgText::Text::OUTLINE );
+
+        if ( symbol->haloOffset().isSet() )
+        {
+            t->setBackdropOffset( *symbol->haloOffset(), *symbol->haloOffset() );
+        }
     }
     else if ( !symbol )
     {
@@ -175,7 +180,7 @@ AnnotationUtils::createImageGeometry(osg::Image*       image,
     osg::StateSet* dstate = new osg::StateSet;
     dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
     dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
-    dstate->setMode(GL_BLEND, 1);
+    //dstate->setMode(GL_BLEND, 1); // redundant. AnnotationNode sets blending.
     dstate->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON);   
 
     // set up the geoset.
@@ -245,11 +250,12 @@ AnnotationUtils::createHighlightUniform()
     return u;
 }
 
+#if 0
 void
-AnnotationUtils::installAnnotationProgram( osg::StateSet* stateSet )
+AnnotationUtils::installAnnotationProgram(osg::StateSet*                     stateSet,
+                                          osg::StateAttribute::OverrideValue qualifier)
 {
-    static Threading::Mutex           s_mutex;
-    //static osg::ref_ptr<osg::Program> s_program;
+    static Threading::Mutex             s_mutex;
     static osg::ref_ptr<VirtualProgram> s_program;
     static osg::ref_ptr<osg::Uniform>   s_samplerUniform;
     static osg::ref_ptr<osg::Uniform>   s_defaultFadeUniform;
@@ -262,6 +268,9 @@ AnnotationUtils::installAnnotationProgram( osg::StateSet* stateSet )
         {
             std::string vertSource =
                 "#version " GLSL_VERSION_STR "\n"
+#ifdef OSG_GLES2_AVAILABLE
+                "precision mediump float;\n"
+#endif
                 //NOTE: Tom commented this out; I commented it back in b/c that breaks things 
                 //( //not sure why but these arn't merging properly, osg earth color funcs decalre it anyhow for now)
                 "varying vec4 osg_FrontColor; \n"
@@ -279,33 +288,30 @@ AnnotationUtils::installAnnotationProgram( osg::StateSet* stateSet )
 #endif
                 "uniform float " << UNIFORM_FADE()      << "; \n"
                 "uniform bool  " << UNIFORM_IS_TEXT()   << "; \n"
-                //"uniform bool  " << UNIFORM_HIGHLIGHT() << "; \n"
                 "uniform sampler2D oeAnno_tex0; \n"
-                "varying vec4 osg_FrontColor; \n"
                 "varying vec4 oeAnno_texCoord; \n"
+                "varying vec4 osg_FrontColor; \n"
                 "void oeAnno_fragColoring( inout vec4 color ) \n"
                 "{ \n"
+                "    color = osg_FrontColor; \n"
                 "    if (" << UNIFORM_IS_TEXT() << ") \n"
                 "    { \n"
                 "        float alpha = texture2D(oeAnno_tex0, oeAnno_texCoord.st).a; \n"
-                "        color = vec4(osg_FrontColor.rgb, osg_FrontColor.a * alpha * " << UNIFORM_FADE() << "); \n"
+                "        color = vec4(color.rgb, color.a * alpha * " << UNIFORM_FADE() << "); \n"
                 "    } \n"
                 "    else \n"
                 "    { \n"
-                "        color = osg_FrontColor * texture2D(oeAnno_tex0, oeAnno_texCoord.st) * vec4(1,1,1," << UNIFORM_FADE() << "); \n"
+                "        color = color * texture2D(oeAnno_tex0, oeAnno_texCoord.st) * vec4(1,1,1," << UNIFORM_FADE() << "); \n"
                 "    } \n"
-                //"    if (" << UNIFORM_HIGHLIGHT() << ") \n"
-                //"    { \n"
-                //"        color = vec4(color.r*1.5, color.g*0.5, color.b*0.25, color.a); \n"
-                //"    } \n"
                 "} \n";
 
             s_program = new VirtualProgram();
             s_program->setName( PROGRAM_NAME() );
+            s_program->setInheritShaders( false );
             s_program->setUseLightingShaders( false );
             s_program->installDefaultColoringShaders();
             s_program->setFunction( "oeAnno_vertColoring", vertSource, ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
-            s_program->setFunction( "oeAnno_fragColoring", fragSource, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+            s_program->setFunction( "oeAnno_fragColoring", fragSource, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING, 2.0f );
 
             s_samplerUniform = new osg::Uniform(osg::Uniform::SAMPLER_2D, "oeAnno_tex0");
             s_samplerUniform->set( 0 );
@@ -314,54 +320,15 @@ AnnotationUtils::installAnnotationProgram( osg::StateSet* stateSet )
 
             s_defaultIsTextUniform = new osg::Uniform(osg::Uniform::BOOL, "oeAnno_isText");
             s_defaultIsTextUniform->set( false );
-
-#if 0
-            std::string vert_source = // Stringify() <<
-                "#version 110 \n"
-                "void main() { \n"
-                "    osg_FrontColor = gl_Color; \n"
-                "    osg_TexCoord[0] = gl_MultiTexCoord0; \n"
-                "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n"
-                "} \n";
-
-            std::string frag_source = Stringify() <<
-#ifdef OSG_GLES2_AVAILABLE
-                "precision mediump float;\n"
-#endif
-                "uniform float     " << UNIFORM_FADE()      << "; \n"
-                "uniform bool      " << UNIFORM_IS_TEXT()   << "; \n"
-                "uniform bool      " << UNIFORM_HIGHLIGHT() << "; \n"
-                "uniform sampler2D tex0; \n"
-                "varying vec4 osg_TexCoord[" << Registry::instance()->getCapabilities().getMaxGPUTextureCoordSets() << "];\n"
-                "varying vec4 osg_FrontColor; \n"
-                "void main() { \n"
-                "    vec4 color; \n"
-                "    if (" << UNIFORM_IS_TEXT() << ") { \n"
-                "        float alpha = texture2D(tex0,osg_TexCoord[0].st).a; \n"
-                "        color = vec4( osg_FrontColor.rgb, osg_FrontColor.a * alpha * " << UNIFORM_FADE() << "); \n"
-                "    } \n"
-                "    else { \n"
-                "        color = osg_FrontColor * texture2D(tex0,osg_TexCoord[0].st) * vec4(1,1,1," << UNIFORM_FADE() << "); \n"
-                "    } \n"
-                "    if (" << UNIFORM_HIGHLIGHT() << ") { \n"
-                "        color = vec4(color.r*1.5, color.g*0.5, color.b*0.25, color.a); \n"
-                "    } \n"
-                "    gl_FragColor = color; \n"
-                "} \n";
-
-            s_program = new osg::Program();
-            s_program->setName( PROGRAM_NAME() );
-            s_program->addShader( new osg::Shader(osg::Shader::VERTEX,   vert_source) );
-            s_program->addShader( new osg::Shader(osg::Shader::FRAGMENT, frag_source) );
-#endif
         }
     }
 
-    stateSet->setAttributeAndModes( s_program.get() );
+    stateSet->setAttributeAndModes( s_program.get(), qualifier );
     stateSet->addUniform( s_samplerUniform.get() );
     stateSet->addUniform( s_defaultFadeUniform.get() );
     stateSet->addUniform( s_defaultIsTextUniform.get() );
 }
+#endif
 
 //-------------------------------------------------------------------------
 
@@ -667,7 +634,7 @@ AnnotationUtils::createFullScreenQuad( const osg::Vec4& color )
 
     osg::StateSet* s = geom->getOrCreateStateSet();
     s->setMode(GL_LIGHTING,0);
-    s->setMode(GL_BLEND,1);
+    //s->setMode(GL_BLEND,1); // redundant. AnnotationNode sets blend.
     s->setMode(GL_DEPTH_TEST,0);
     s->setMode(GL_CULL_FACE,0);
     s->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), 1 );
diff --git a/src/osgEarthAnnotation/CircleNode.cpp b/src/osgEarthAnnotation/CircleNode.cpp
index e3b77ba..a5a8f18 100644
--- a/src/osgEarthAnnotation/CircleNode.cpp
+++ b/src/osgEarthAnnotation/CircleNode.cpp
@@ -125,6 +125,8 @@ CircleNode::rebuild()
         }
 
         applyStyle( _style );
+
+        setLightingIfNotSet( false );
     }
 
     setDecoration( currentDecoration );
@@ -139,7 +141,7 @@ OSGEARTH_REGISTER_ANNOTATION( circle, osgEarth::Annotation::CircleNode );
 CircleNode::CircleNode(MapNode*              mapNode,
                        const Config&         conf,
                        const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode ),
+LocalizedNode( mapNode, conf ),
 _radius      ( 1.0, Units::KILOMETERS ),
 _draped      ( false ),
 _numSegments ( 0 )
diff --git a/src/osgEarthAnnotation/EllipseNode.cpp b/src/osgEarthAnnotation/EllipseNode.cpp
index 774e4be..a4ae020 100644
--- a/src/osgEarthAnnotation/EllipseNode.cpp
+++ b/src/osgEarthAnnotation/EllipseNode.cpp
@@ -162,6 +162,8 @@ EllipseNode::rebuild()
         }
 
         applyStyle( _style );
+
+        setLightingIfNotSet( false );
     }
 
     setDecoration( currentDecoration );
@@ -177,7 +179,7 @@ OSGEARTH_REGISTER_ANNOTATION( ellipse, osgEarth::Annotation::EllipseNode );
 EllipseNode::EllipseNode(MapNode*              mapNode,
                          const Config&         conf,
                          const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode ),
+LocalizedNode( mapNode, conf ),
 _draped      ( false ),
 _numSegments ( 0 )
 {
diff --git a/src/osgEarthAnnotation/FeatureNode.cpp b/src/osgEarthAnnotation/FeatureNode.cpp
index 8491951..4186d58 100644
--- a/src/osgEarthAnnotation/FeatureNode.cpp
+++ b/src/osgEarthAnnotation/FeatureNode.cpp
@@ -31,6 +31,7 @@
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Utils>
 #include <osgEarth/Registry>
+#include <osgEarth/ShaderGenerator>
 
 #include <osg/BoundingSphere>
 #include <osg/Polytope>
@@ -121,13 +122,16 @@ FeatureNode::init()
             {
                 this->addChild( _attachPoint );
             }
-        }
 
-        // workaround until we can auto-clamp extruded/sub'd geometries.
-        if ( autoClamping )
-        {
-            applyStyle( *_feature->style() );
-            clampMesh( getMapNode()->getTerrain()->getGraph() );
+            // workaround until we can auto-clamp extruded/sub'd geometries.
+            if ( autoClamping )
+            {
+                applyStyle( *_feature->style() );
+
+                setLightingIfNotSet( _feature->style()->has<ExtrusionSymbol>() );
+
+                clampMesh( getMapNode()->getTerrain()->getGraph() );
+            }
         }
     }
 }
@@ -207,7 +211,7 @@ OSGEARTH_REGISTER_ANNOTATION( feature, osgEarth::Annotation::FeatureNode );
 FeatureNode::FeatureNode(MapNode*              mapNode,
                          const Config&         conf,
                          const osgDB::Options* dbOptions ) :
-AnnotationNode( mapNode )
+AnnotationNode( mapNode, conf )
 {
     osg::ref_ptr<Geometry> geom;
     if ( conf.hasChild("geometry") )
diff --git a/src/osgEarthAnnotation/ImageOverlay.cpp b/src/osgEarthAnnotation/ImageOverlay.cpp
index 5591103..dd17251 100644
--- a/src/osgEarthAnnotation/ImageOverlay.cpp
+++ b/src/osgEarthAnnotation/ImageOverlay.cpp
@@ -26,7 +26,9 @@
 #include <osgEarth/NodeUtils>
 #include <osgEarth/ImageUtils>
 #include <osgEarth/DrapeableNode>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
 #include <osg/Geode>
 #include <osg/ShapeDrawable>
 #include <osg/Texture2D>
@@ -56,7 +58,7 @@ namespace
 OSGEARTH_REGISTER_ANNOTATION( imageoverlay, osgEarth::Annotation::ImageOverlay );
 
 ImageOverlay::ImageOverlay(MapNode* mapNode, const Config& conf, const osgDB::Options* dbOptions) :
-AnnotationNode(mapNode),
+AnnotationNode(mapNode, conf),
 _lowerLeft    (10, 10),
 _lowerRight   (20, 10),
 _upperRight   (20, 20),
@@ -191,18 +193,17 @@ ImageOverlay::postCTOR()
 
     d->addChild( _transform );
 
-    // need a shader that supports one texture
-    VirtualProgram* vp = new VirtualProgram();
-    vp->setName( "imageoverlay");
-    vp->installDefaultColoringShaders(1);
-    //vp->installDefaultColoringAndLightingShaders(1);
-    //vp->setInheritShaders(false);
-    d->getOrCreateStateSet()->setAttributeAndModes( vp, 1 );
-    
+    if ( Registry::capabilities().supportsGLSL() )
+    {
+        // need a shader that supports one texture
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setName( "imageoverlay");
+        vp->installDefaultColoringShaders(1);
+        d->getOrCreateStateSet()->setAttributeAndModes( vp, 1 );
+    }
+
     init();    
     ADJUST_UPDATE_TRAV_COUNT( this, 1 );
-
-//    getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
 }
 
 void
@@ -305,6 +306,7 @@ ImageOverlay::init()
         Style style;
         style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN;
         applyStyle( style );
+        setLightingIfNotSet( false );
         clampMesh( getMapNode()->getTerrain()->getGraph() );
     }
 }
diff --git a/src/osgEarthAnnotation/LabelNode b/src/osgEarthAnnotation/LabelNode
index cb42564..7fcd158 100644
--- a/src/osgEarthAnnotation/LabelNode
+++ b/src/osgEarthAnnotation/LabelNode
@@ -90,10 +90,15 @@ namespace osgEarth { namespace Annotation
         const std::string& text() const { return _text; }
 
         /**
-         * Gets the text style.
+         * Gets a copy of the text style.
          */
         const Style style() const { return _style; }
 
+        /**
+         * Sets a new text style
+         */
+        void setStyle( const Style& style );
+
     public: // OrthoNode override
 
         virtual void setAnnotationData( AnnotationData* data );
@@ -103,7 +108,7 @@ namespace osgEarth { namespace Annotation
         virtual Config getConfig() const;
 
     protected:
-        void init();
+        void init(const Style& style);
 
         std::string       _text;
         class osg::Geode* _geode;
diff --git a/src/osgEarthAnnotation/LabelNode.cpp b/src/osgEarthAnnotation/LabelNode.cpp
index c1048fa..b57dd2d 100644
--- a/src/osgEarthAnnotation/LabelNode.cpp
+++ b/src/osgEarthAnnotation/LabelNode.cpp
@@ -22,6 +22,8 @@
 #include <osgEarthAnnotation/AnnotationUtils>
 #include <osgEarthAnnotation/AnnotationRegistry>
 #include <osgEarthSymbology/Color>
+#include <osgEarth/Registry>
+#include <osgEarth/ShaderGenerator>
 #include <osgText/Text>
 #include <osg/Depth>
 #include <osgUtil/IntersectionVisitor>
@@ -43,10 +45,9 @@ LabelNode::LabelNode(MapNode*            mapNode,
 
 OrthoNode( mapNode, position ),
 _text    ( text ),
-_geode   ( 0L ),
-_style   ( style )
+_geode   ( 0L )
 {
-    init();
+    init( style );
 }
 
 LabelNode::LabelNode(MapNode*            mapNode,
@@ -58,66 +59,46 @@ OrthoNode( mapNode, position ),
 _text    ( text ),
 _geode   ( 0L )
 {
-    _style.add( const_cast<TextSymbol*>(symbol) );
-    init();
+    Style style;
+    style.add( const_cast<TextSymbol*>(symbol) );
+    init( style );
 }
 
-#if 0
-LabelNode::LabelNode(const SpatialReference* mapSRS,
-                     const GeoPoint&         position,
-                     const std::string&      text,
-                     const TextSymbol*       symbol ) :
-
-OrthoNode( mapSRS, position ),
-_text    ( text ),
-_geode   ( 0L )
-{
-    _style.add( const_cast<TextSymbol*>(symbol) );
-    init();
-}
-#endif
-
 LabelNode::LabelNode(const std::string&  text,
                      const Style&        style ) :
 OrthoNode(),
 _text    ( text ),
-_geode   ( 0L ),
-_style   ( style )
+_geode   ( 0L )
 {
-    init();
+    init( style );
 }
 
 LabelNode::LabelNode(MapNode*            mapNode,
                      const GeoPoint&     position,
                      const Style&        style ) :
 OrthoNode( mapNode, position ),
-_geode   ( 0L ),
-_style   ( style )
+_geode   ( 0L )
 {
-    init();
+    init( style );
 }
 
 void
-LabelNode::init()
+LabelNode::init( const Style& style )
 {
-    const TextSymbol* symbol = _style.get<TextSymbol>();
-
-    if ( _text.empty() )
-        _text = symbol->content()->eval();
-
     _geode = new osg::Geode();
-
-    osg::Drawable* t = AnnotationUtils::createTextDrawable( _text, symbol, osg::Vec3(0,0,0) );
-    _geode->addDrawable(t);
+    getAttachPoint()->addChild( _geode );
 
     osg::StateSet* stateSet = _geode->getOrCreateStateSet();
     stateSet->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), 1 );
 
-    getAttachPoint()->addChild( _geode );
+    setStyle( style );
 
-    AnnotationUtils::installAnnotationProgram( stateSet );
+    applyStyle( style );
 
-    applyStyle( _style );
+    setLightingIfNotSet( false );
+
+    ShaderGenerator gen( Registry::stateSetCache() );
+    this->accept( gen );
 }
 
 void
@@ -138,6 +119,32 @@ LabelNode::setText( const std::string& text )
 }
 
 void
+LabelNode::setStyle( const Style& style )
+{
+    if ( !_dynamic && getNumParents() > 0 )
+    {
+        OE_WARN << LC << "Illegal state: cannot change a LabelNode that is not dynamic" << std::endl;
+        return;
+    }
+
+    _geode->removeDrawables( 0, _geode->getNumDrawables() );
+
+    _style = style;
+
+    const TextSymbol* symbol = _style.get<TextSymbol>();
+
+    if ( _text.empty() )
+        _text = symbol->content()->eval();
+
+    osg::Drawable* t = AnnotationUtils::createTextDrawable( _text, symbol, osg::Vec3(0,0,0) );
+    _geode->addDrawable(t);
+
+    applyStyle( _style );
+
+    setLightingIfNotSet( false );
+}
+
+void
 LabelNode::setAnnotationData( AnnotationData* data )
 {
     OrthoNode::setAnnotationData( data );
@@ -174,10 +181,12 @@ LabelNode::LabelNode(MapNode*               mapNode,
                      const osgDB::Options* dbOptions ) :
 OrthoNode( mapNode, GeoPoint::INVALID )
 {
-    conf.getObjIfSet( "style",  _style );
-    conf.getIfSet   ( "text",   _text );
+    optional<Style> style;
+
+    conf.getObjIfSet( "style", style );
+    conf.getIfSet   ( "text",  _text );
 
-    init();
+    init( *style );
 
     if ( conf.hasChild("position") )
         setPosition( GeoPoint(conf.child("position")) );
diff --git a/src/osgEarthAnnotation/LocalGeometryNode.cpp b/src/osgEarthAnnotation/LocalGeometryNode.cpp
index 393267a..dfe2f2f 100644
--- a/src/osgEarthAnnotation/LocalGeometryNode.cpp
+++ b/src/osgEarthAnnotation/LocalGeometryNode.cpp
@@ -63,6 +63,8 @@ _draped      ( draped )
 
         // this will activate the clamping logic
         applyStyle( style );
+
+        setLightingIfNotSet( style.has<ExtrusionSymbol>() );
     }
 }
 
@@ -99,7 +101,7 @@ OSGEARTH_REGISTER_ANNOTATION( local_geometry, osgEarth::Annotation::LocalGeometr
 LocalGeometryNode::LocalGeometryNode(MapNode*              mapNode,
                                      const Config&         conf,
                                      const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode )
+LocalizedNode( mapNode, conf )
 {
     if ( conf.hasChild("geometry") )
     {
diff --git a/src/osgEarthAnnotation/LocalizedNode b/src/osgEarthAnnotation/LocalizedNode
index cf476c6..fc7c3da 100644
--- a/src/osgEarthAnnotation/LocalizedNode
+++ b/src/osgEarthAnnotation/LocalizedNode
@@ -44,17 +44,12 @@ namespace osgEarth { namespace Annotation
          * @param is2D    Whether to autotransform the node to face/scale to the screen
          */
         LocalizedNode( 
-            MapNode*          mapNode  =0L,
+            MapNode*          mapNode,
             const GeoPoint&   pos      =GeoPoint::INVALID,
             bool              is2D     =false );
 
         LocalizedNode(
             MapNode*          mapNode,
-            const osg::Vec3d& pos,
-            bool              is2D    =false );
-
-        LocalizedNode(
-            MapNode*          mapNode,
             const Config&     conf );
 
         virtual ~LocalizedNode() { }
@@ -133,9 +128,14 @@ namespace osgEarth { namespace Annotation
 
             
         // copy constructor
+        LocalizedNode() { }
         LocalizedNode(const LocalizedNode& rhs, const osg::CopyOp& op=osg::CopyOp::DEEP_COPY_ALL) { }
 
         bool updateTransforms(const GeoPoint& absPt, osg::Node* patch =0L);
+
+    private:
+
+        void init( MapNode* mapNode, const GeoPoint& position );
         
     };
 
diff --git a/src/osgEarthAnnotation/LocalizedNode.cpp b/src/osgEarthAnnotation/LocalizedNode.cpp
index 4b13c80..eef0fb2 100644
--- a/src/osgEarthAnnotation/LocalizedNode.cpp
+++ b/src/osgEarthAnnotation/LocalizedNode.cpp
@@ -30,14 +30,30 @@ using namespace osgEarth;
 using namespace osgEarth::Annotation;
 
 
-LocalizedNode::LocalizedNode(MapNode*                mapNode,
-                             const GeoPoint&         position,
-                             bool                    is2D ) :
+LocalizedNode::LocalizedNode(MapNode*        mapNode,
+                             const GeoPoint& position,
+                             bool            is2D ) :
 PositionedAnnotationNode( mapNode ),
 _horizonCulling         ( false ),
 _autoTransform          ( is2D ),
 _scale                  ( 1.0f, 1.0f, 1.0f )
 {
+    init( mapNode, position );
+}
+
+LocalizedNode::LocalizedNode(MapNode* mapNode, const Config& conf) :
+PositionedAnnotationNode(mapNode, conf),
+_horizonCulling         ( false ),
+_autoTransform          ( false ),
+_scale                  ( 1.0f, 1.0f, 1.0f )
+{
+    init( mapNode, GeoPoint::INVALID );
+}
+
+
+void
+LocalizedNode::init( MapNode* mapNode, const GeoPoint& position )
+{
     if ( _autoTransform )
     {
         osg::AutoTransform* at = new osg::AutoTransform();
@@ -53,7 +69,7 @@ _scale                  ( 1.0f, 1.0f, 1.0f )
         this->getOrCreateStateSet()->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
     }
     
-    this->getOrCreateStateSet()->setMode( GL_BLEND, 1 );
+    //this->getOrCreateStateSet()->setMode( GL_BLEND, 1 );
 
     setHorizonCulling( true );
 
diff --git a/src/osgEarthAnnotation/ModelNode.cpp b/src/osgEarthAnnotation/ModelNode.cpp
index 8430c7a..f136619 100644
--- a/src/osgEarthAnnotation/ModelNode.cpp
+++ b/src/osgEarthAnnotation/ModelNode.cpp
@@ -22,7 +22,9 @@
 #include <osgEarthSymbology/Style>
 #include <osgEarthSymbology/InstanceSymbol>
 #include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
 #include <osgEarth/ShaderGenerator>
+#include <osgEarth/VirtualProgram>
 
 #define LC "[ModelNode] "
 
@@ -60,28 +62,51 @@ ModelNode::init(const osgDB::Options* dbOptions)
 
     if ( sym.valid() )
     {
-        if ( sym->url().isSet() )
+        if ( ( sym->url().isSet() ) || (sym->getModel() != NULL) )
         {
-            URI uri( sym->url()->eval(), sym->url()->uriContext() );
-            osg::Node* node = 0L;
-            
-            if ( sym->uriAliasMap()->empty() )
-            {
-                node = uri.getNode( dbOptions );
-            }
-            else
+            // Try to get a model from symbol
+            osg::ref_ptr<osg::Node> node = sym->getModel();
+
+            // Try to get a model from URI
+            if (node.valid() == false)
             {
-                // install an alias map if there's one in the symbology.
-                osg::ref_ptr<osgDB::Options> tempOptions = Registry::instance()->cloneOrCreateOptions(dbOptions);
-                tempOptions->setReadFileCallback( new URIAliasMapReadCallback(*sym->uriAliasMap(), uri.full()) );
-                node = uri.getNode( tempOptions.get() );
+                URI uri( sym->url()->eval(), sym->url()->uriContext() );
+
+                if ( sym->uriAliasMap()->empty() )
+                {
+                    node = uri.getNode( dbOptions );
+                }
+                else
+                {
+                    // install an alias map if there's one in the symbology.
+                    osg::ref_ptr<osgDB::Options> tempOptions = Registry::instance()->cloneOrCreateOptions(dbOptions);
+                    tempOptions->setReadFileCallback( new URIAliasMapReadCallback(*sym->uriAliasMap(), uri.full()) );
+                    node = uri.getNode( tempOptions.get() );
+                }
+
+                if (node.valid() == false)
+                {
+                    OE_WARN << LC << "No model and failed to load data from " << uri.full() << std::endl;
+                }
             }
 
-            if ( node )
+            if (node.valid() == true)
             {
-                ShaderGenerator gen;
-                node->accept( gen );
+                if ( Registry::capabilities().supportsGLSL() )
+                {
+                    // generate shader code for the loaded model:
+                    ShaderGenerator gen( Registry::stateSetCache() );
+                    node->accept( gen );
 
+                    // need a top-level shader too:
+                    VirtualProgram* vp = new VirtualProgram();
+                    vp->installDefaultColoringAndLightingShaders();
+                    this->getOrCreateStateSet()->setAttributeAndModes( vp, 1 );
+
+                    node->addCullCallback( new UpdateLightingUniformsHelper() );
+                }
+
+                // attach to the transform:
                 getTransform()->addChild( node );
                 this->addChild( getTransform() );
 
@@ -108,12 +133,12 @@ ModelNode::init(const osgDB::Options* dbOptions)
             }
             else
             {
-                OE_WARN << LC << "Failed to load data from " << uri.full() << std::endl;
+                OE_WARN << LC << "No model" << std::endl;
             }
         }
         else
         {
-            OE_WARN << LC << "Symbology: no URI" << std::endl;
+            OE_WARN << LC << "Symbology: no URI or model" << std::endl;
         }
     }
     else
@@ -128,7 +153,7 @@ OSGEARTH_REGISTER_ANNOTATION( model, osgEarth::Annotation::ModelNode );
 
 
 ModelNode::ModelNode(MapNode* mapNode, const Config& conf, const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode )
+LocalizedNode( mapNode, conf )
 {
     conf.getObjIfSet( "style", _style );
 
diff --git a/src/osgEarthAnnotation/OrthoNode b/src/osgEarthAnnotation/OrthoNode
index 47c0580..3e5887b 100644
--- a/src/osgEarthAnnotation/OrthoNode
+++ b/src/osgEarthAnnotation/OrthoNode
@@ -53,15 +53,10 @@ namespace osgEarth { namespace Annotation
             MapNode*        mapNode, 
             const GeoPoint& position );
 
+
         /**
-         * Constructs an ortho node that resides at an absolute position on the map
+         * dtor.
          */
-#if 0
-        OrthoNode(
-            const SpatialReference* mapSRS, 
-            const GeoPoint&         position );
-#endif
-
         virtual ~OrthoNode() { }
 
         /**
diff --git a/src/osgEarthAnnotation/PlaceNode.cpp b/src/osgEarthAnnotation/PlaceNode.cpp
index 725842d..84a9460 100644
--- a/src/osgEarthAnnotation/PlaceNode.cpp
+++ b/src/osgEarthAnnotation/PlaceNode.cpp
@@ -24,6 +24,7 @@
 #include <osgEarthFeatures/LabelSource>
 #include <osgEarth/Utils>
 #include <osgEarth/Registry>
+#include <osgEarth/ShaderGenerator>
 
 #include <osg/Depth>
 #include <osgText/Text>
@@ -187,12 +188,15 @@ PlaceNode::init(const osgDB::Options* dbOptions)
     osg::StateSet* stateSet = _geode->getOrCreateStateSet();
     stateSet->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), 1 );
 
-    AnnotationUtils::installAnnotationProgram( stateSet );
-
     getAttachPoint()->addChild( _geode );
 
     // for clamping
     applyStyle( _style );
+
+    setLightingIfNotSet( false );
+
+    ShaderGenerator gen( Registry::stateSetCache() );
+    this->accept( gen );
 }
 
 
@@ -256,7 +260,7 @@ OSGEARTH_REGISTER_ANNOTATION( place, osgEarth::Annotation::PlaceNode );
 PlaceNode::PlaceNode(MapNode*              mapNode,
                      const Config&         conf,
                      const osgDB::Options* dbOptions) :
-OrthoNode( mapNode, GeoPoint::INVALID )
+OrthoNode( mapNode, conf )
 {
     conf.getObjIfSet( "style",  _style );
     conf.getIfSet   ( "text",   _text );
diff --git a/src/osgEarthAnnotation/RectangleNode.cpp b/src/osgEarthAnnotation/RectangleNode.cpp
index 1b34a66..19ddc62 100644
--- a/src/osgEarthAnnotation/RectangleNode.cpp
+++ b/src/osgEarthAnnotation/RectangleNode.cpp
@@ -32,13 +32,12 @@ using namespace osgEarth::Features;
 using namespace osgEarth::Symbology;
 
 
-RectangleNode::RectangleNode(
-            MapNode*          mapNode,
-            const GeoPoint&   position,
-            const Linear&     width,
-            const Linear&     height,
-            const Style&      style,
-            bool              draped ) :
+RectangleNode::RectangleNode(MapNode*          mapNode,
+                             const GeoPoint&   position,
+                             const Linear&     width,
+                             const Linear&     height,
+                             const Style&      style,
+                             bool              draped ) :
 LocalizedNode( mapNode, position, false ),
 _width       ( width ),
 _height      ( height ),
@@ -362,6 +361,8 @@ RectangleNode::rebuild()
         }
 
         applyStyle( _style );
+
+        setLightingIfNotSet( false );
     }
 
     setDecoration( currentDecoration );
@@ -377,7 +378,7 @@ OSGEARTH_REGISTER_ANNOTATION( rectangle, osgEarth::Annotation::RectangleNode );
 RectangleNode::RectangleNode(MapNode*              mapNode,
                              const Config&         conf,
                              const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode ),
+LocalizedNode( mapNode, conf ),
 _draped      ( false )
 {
     conf.getObjIfSet( "width", _width );
diff --git a/src/osgEarthAnnotation/TrackNode.cpp b/src/osgEarthAnnotation/TrackNode.cpp
index f42777f..feac0ad 100644
--- a/src/osgEarthAnnotation/TrackNode.cpp
+++ b/src/osgEarthAnnotation/TrackNode.cpp
@@ -20,6 +20,8 @@
 #include <osgEarthAnnotation/TrackNode>
 #include <osgEarthAnnotation/AnnotationUtils>
 #include <osgEarth/MapNode>
+#include <osgEarth/Registry>
+#include <osgEarth/ShaderGenerator>
 #include <osg/Depth>
 #include <osgText/Text>
 
@@ -93,9 +95,12 @@ TrackNode::init( const TrackNodeFieldSchema& schema )
     osg::StateSet* stateSet = _geode->getOrCreateStateSet();
     stateSet->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), 1 );
 
-    AnnotationUtils::installAnnotationProgram( stateSet );
+    setLightingIfNotSet( false );
 
     getAttachPoint()->addChild( _geode );
+
+    ShaderGenerator gen( Registry::stateSetCache() );
+    this->accept( gen );
 }
 
 void
@@ -113,7 +118,7 @@ TrackNode::setFieldValue( const std::string& name, const osgText::String& value
             if (drawable->getDataVariance() == osg::Object::DYNAMIC || this->getNumParents() == 0)
             {
                 // btw, setText checks for assigning an equal value, so we don't have to
-                drawable->setText( value );             
+                drawable->setText( value );
             }
             else
             {
diff --git a/src/osgEarthDrivers/earth/EarthFileSerializer b/src/osgEarthDrivers/earth/EarthFileSerializer
index e96d66e..749b5c2 100644
--- a/src/osgEarthDrivers/earth/EarthFileSerializer
+++ b/src/osgEarthDrivers/earth/EarthFileSerializer
@@ -22,20 +22,24 @@
 #include <osgEarth/Config>
 #include <osgEarth/MapNode>
 
-using namespace osgEarth;
-
-class EarthFileSerializer1
+namespace osgEarth_osgearth
 {
-public:
-    MapNode* deserialize( const Config& conf, const std::string& referenceURI ) const;
-};
+    using namespace osgEarth;
 
-class EarthFileSerializer2
-{
-public:
-    MapNode* deserialize( const Config& conf, const std::string& referenceURI ) const;
+    class EarthFileSerializer1
+    {
+    public:
+        MapNode* deserialize( const Config& conf, const std::string& referenceURI ) const;
+    };
+
+    class EarthFileSerializer2
+    {
+    public:
+        MapNode* deserialize( const Config& conf, const std::string& referenceURI ) const;
+
+        Config serialize( MapNode* mapNode ) const;
+    };
 
-    Config serialize( MapNode* mapNode ) const;
-};
+} // namespace osgEarth_osgearth
 
 #endif // OSGEARTH_EARTH_PLUGIN_SERIALIZER_H
diff --git a/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp b/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp
index 4428240..d69f68b 100644
--- a/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp
+++ b/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp
@@ -18,6 +18,7 @@
  */
 #include "EarthFileSerializer"
 
+using namespace osgEarth_osgearth;
 using namespace osgEarth;
 
 MapNode*
diff --git a/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp b/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp
index f651f08..3b6cefc 100644
--- a/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp
+++ b/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp
@@ -18,7 +18,9 @@
  */
 #include "EarthFileSerializer"
 #include <osgEarth/FileUtils>
+#include <osgEarth/MapFrame>
 
+using namespace osgEarth_osgearth;
 using namespace osgEarth;
 
 MapNode*
diff --git a/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp b/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp
index dfc5214..dc15657 100644
--- a/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp
+++ b/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp
@@ -28,6 +28,7 @@
 #include <sstream>
 #include <osgEarthUtil/Common>
 
+using namespace osgEarth_osgearth;
 using namespace osgEarth;
 
 #define LC "[ReaderWriterEarth] "
diff --git a/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique b/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique
index 957ebe8..5303702 100644
--- a/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique
+++ b/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique
@@ -25,55 +25,59 @@
 #include <osg/NodeVisitor>
 #include <osgEarth/Progress>
 
-using namespace osgEarth;
+namespace osgEarth_engine_osgterrain
+{
+    using namespace osgEarth;
 
-class Tile;
-class TileUpdate;
+    class Tile;
+    class TileUpdate;
 
-class TerrainTechnique : public osg::Object
-{
-public:
-    virtual void init() =0;
+    class TerrainTechnique : public osg::Object
+    {
+    public:
+        virtual void init() =0;
 
-    virtual void traverse( osg::NodeVisitor& nv ) =0;
+        virtual void traverse( osg::NodeVisitor& nv ) =0;
 
-    virtual void releaseGLObjects( osg::State* state ) const =0;
+        virtual void releaseGLObjects( osg::State* state ) const =0;
 
-protected:
-    TerrainTechnique() : _tile(0L) { }
+    protected:
+        TerrainTechnique() : _tile(0L) { }
 
-    TerrainTechnique( const TerrainTechnique& rhs, const osg::CopyOp& op ) : _tile(0L) { }
+        TerrainTechnique( const TerrainTechnique& rhs, const osg::CopyOp& op ) : _tile(0L) { }
 
-    /** dtor */
-    virtual ~TerrainTechnique() { }
+        /** dtor */
+        virtual ~TerrainTechnique() { }
 
-    Tile* _tile;
+        Tile* _tile;
 
-    friend class Tile;
-};
+        friend class Tile;
+    };
 
-class CustomTerrainTechnique : public TerrainTechnique
-{
-public:
-    virtual void compile( const TileUpdate& updateSpec, ProgressCallback* progress ) =0;
+    class CustomTerrainTechnique : public TerrainTechnique
+    {
+    public:
+        virtual void compile( const TileUpdate& updateSpec, ProgressCallback* progress ) =0;
+
+        virtual bool applyTileUpdates() =0;
 
-    virtual bool applyTileUpdates() =0;
+        virtual void setParentTile( class Tile* tile ) =0;
 
-    virtual void setParentTile( class Tile* tile ) =0;
+        virtual void setOptimizeTriangleOrientation( bool optimizeTriangleOrientation ) =0;
 
-    virtual void setOptimizeTriangleOrientation( bool optimizeTriangleOrientation ) =0;
+        virtual bool getOptimizeTriangleOrientation() const =0;
 
-    virtual bool getOptimizeTriangleOrientation() const =0;
 
+    protected:
+        CustomTerrainTechnique() { }
 
-protected:
-    CustomTerrainTechnique() { }
+        CustomTerrainTechnique( const CustomTerrainTechnique& rhs, const osg::CopyOp& op )
+            : TerrainTechnique( rhs, op ) { }
 
-    CustomTerrainTechnique( const CustomTerrainTechnique& rhs, const osg::CopyOp& op )
-        : TerrainTechnique( rhs, op ) { }
+        /** dtor */
+        virtual ~CustomTerrainTechnique() { }
+    };
 
-    /** dtor */
-    virtual ~CustomTerrainTechnique() { }
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_EXTENDED_TERRAIN_TECHNIQUE
diff --git a/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback b/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback
index 62cbc7c..73aa43a 100644
--- a/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback
+++ b/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback
@@ -23,59 +23,63 @@
 #include <osg/NodeCallback>
 #include <osg/CullStack>
 
-/**
- * Cull callback that dynamically computes an LOD scale based on
- * distance to the camera and a "fall off" metric. As the fall off
- * increases, farther objects' LOD scale will increase. A good
- * range for the fall-off number is 0..5.
- */
-struct DynamicLODScaleCallback : public osg::NodeCallback 
+namespace osgEarth_engine_osgterrain
 {
-    DynamicLODScaleCallback( float fallOff ) : _fallOff(fallOff) { }
+    /**
+     * Cull callback that dynamically computes an LOD scale based on
+     * distance to the camera and a "fall off" metric. As the fall off
+     * increases, farther objects' LOD scale will increase. A good
+     * range for the fall-off number is 0..5.
+     */
+    struct DynamicLODScaleCallback : public osg::NodeCallback 
+    {
+        DynamicLODScaleCallback( float fallOff ) : _fallOff(fallOff) { }
 
-    /** dtor */
-    virtual ~DynamicLODScaleCallback() { }
+        /** dtor */
+        virtual ~DynamicLODScaleCallback() { }
 
-    void operator()( osg::Node* node, osg::NodeVisitor* nv )
-    {
-        osg::CullStack* cs = dynamic_cast<osg::CullStack*>(nv);
-        if ( cs )
+        void operator()( osg::Node* node, osg::NodeVisitor* nv )
         {
-            osg::LOD* lod = static_cast<osg::LOD*>( node );
-            osg::Vec3 center = lod->getCenter(); 
+            osg::CullStack* cs = dynamic_cast<osg::CullStack*>(nv);
+            if ( cs )
+            {
+                osg::LOD* lod = static_cast<osg::LOD*>( node );
+                osg::Vec3 center = lod->getCenter(); 
 
-            osg::Vec3 eye = nv->getEyePoint();
-            osg::Vec3 eyeVec = eye; eyeVec.normalize();
-            float has = osg::clampAbove( eye.length() - 6356752.3142f, 0.0f );
-            float centerToEye = nv->getDistanceToViewPoint(center, false);
-            float bsToEye = centerToEye - lod->getChild(0)->getBound().radius();
+                osg::Vec3 eye = nv->getEyePoint();
+                osg::Vec3 eyeVec = eye; eyeVec.normalize();
+                float has = osg::clampAbove( eye.length() - 6356752.3142f, 0.0f );
+                float centerToEye = nv->getDistanceToViewPoint(center, false);
+                float bsToEye = centerToEye - lod->getChild(0)->getBound().radius();
 
-            float scaleAdj = 1.0f;
-            if ( bsToEye > has )
+                float scaleAdj = 1.0f;
+                if ( bsToEye > has )
+                {
+                    float denom = osg::maximum(0.1f, (1.0f/_fallOff)) * 10000.0f;
+                    scaleAdj = osg::clampBetween( log10f(bsToEye/denom), 1.0f, 3.0f );
+                    
+                    //OE_INFO << LC 
+                    //    << std::fixed
+                    //    << "centerToEye=" << centerToEye 
+                    //    << ", bsToEye=" << bsToEye
+                    //    << ", scaleAdj=" << scaleAdj
+                    //    << std::endl;
+                }
+
+                float lodScale = cs->getLODScale();
+                cs->setLODScale( lodScale * scaleAdj );
+                traverse( node, nv );
+                cs->setLODScale( lodScale );
+            }
+            else
             {
-                float denom = osg::maximum(0.1f, (1.0f/_fallOff)) * 10000.0f;
-                scaleAdj = osg::clampBetween( log10f(bsToEye/denom), 1.0f, 3.0f );
-                
-                //OE_INFO << LC 
-                //    << std::fixed
-                //    << "centerToEye=" << centerToEye 
-                //    << ", bsToEye=" << bsToEye
-                //    << ", scaleAdj=" << scaleAdj
-                //    << std::endl;
+                traverse( node, nv );
             }
-
-            float lodScale = cs->getLODScale();
-            cs->setLODScale( lodScale * scaleAdj );
-            traverse( node, nv );
-            cs->setLODScale( lodScale );
-        }
-        else
-        {
-            traverse( node, nv );
         }
-    }
 
-    float _fallOff;
-};
+        float _fallOff;
+    };
+
+} // namespace osgEarth_engine_osgterrain
 
 #endif //OSGEARTH_ENGINE_OSGTERRAIN_DYNAMIC_LOD_SCALE_CALLBACK_H
diff --git a/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback b/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
index 3b06645..b826cb6 100644
--- a/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
+++ b/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
@@ -25,56 +25,60 @@
 #include <osgEarth/Export>
 
 
+namespace osgEarth_engine_osgterrain
+{
 #define USE_FILELOCATIONCALLBACK OSG_MIN_VERSION_REQUIRED(2,9,5)
 
 #if USE_FILELOCATIONCALLBACK
 
-/**
- * A database pager callback that determines if the given filename is cached or not
- */
-class FileLocationCallback : public osgDB::FileLocationCallback
-{
-public:
-    FileLocationCallback() { }
-
-    /** dtor */
-    virtual ~FileLocationCallback() { }
-
-    virtual Location fileLocation(const std::string& filename, const osgDB::Options* options)
+    /**
+     * A database pager callback that determines if the given filename is cached or not
+     */
+    class FileLocationCallback : public osgDB::FileLocationCallback
     {
-        Location result = REMOTE_FILE;
-        //OE_NOTICE<<"fileLocation = "<<filename<<std::endl;
-
-        unsigned int lod, x, y, id;
-        sscanf(filename.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &id);
+    public:
+        FileLocationCallback() { }
 
-        osg::ref_ptr<OSGTerrainEngineNode> engine;
-        OSGTerrainEngineNode::getEngineByUID( (UID)id, engine );
+        /** dtor */
+        virtual ~FileLocationCallback() { }
 
-        if ( engine.valid() )
+        virtual Location fileLocation(const std::string& filename, const osgDB::Options* options)
         {
-            const osgEarth::Profile* profile = engine->getMap()->getProfile();
-            osgEarth::TileKey mapKey( lod, x, y, profile );
+            Location result = REMOTE_FILE;
+            //OE_NOTICE<<"fileLocation = "<<filename<<std::endl;
 
-            result = LOCAL_FILE;
+            unsigned int lod, x, y, id;
+            sscanf(filename.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &id);
 
-            MapFrame mapf( engine->getMap() );
-            for( unsigned i=0; i<4; ++i )
+            osg::ref_ptr<OSGTerrainEngineNode> engine;
+            OSGTerrainEngineNode::getEngineByUID( (UID)id, engine );
+
+            if ( engine.valid() )
             {
-                TileKey childKey = mapKey.createChildKey( i );
-                if ( !mapf.isCached( childKey ) )
+                const osgEarth::Profile* profile = engine->getMap()->getProfile();
+                osgEarth::TileKey mapKey( lod, x, y, profile );
+
+                result = LOCAL_FILE;
+
+                MapFrame mapf( engine->getMap() );
+                for( unsigned i=0; i<4; ++i )
                 {
-                    result = REMOTE_FILE;
-                    break;
+                    TileKey childKey = mapKey.createChildKey( i );
+                    if ( !mapf.isCached( childKey ) )
+                    {
+                        result = REMOTE_FILE;
+                        break;
+                    }
                 }
             }
+
+            return result;
         }
 
-        return result;
-    }
+        virtual bool useFileCache() const { return false; }
+    };
 
-    virtual bool useFileCache() const { return false; }
-};
+} //namespace osgEarth_engine_osgterrain
 
 #endif // USE_FILELOCATIONCALLBACK
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory
index 9fb3cca..101a5c9 100644
--- a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory
@@ -23,24 +23,28 @@
 #include <osgEarth/TileKey>
 #include <osg/Node>
 
-using namespace osgEarth;
-
-/**
- * A scene graph node that holds a terrain tile (as the single child of its
- * osg::Group) and can load subtiles when they come into LOD range.
- */
-class KeyNodeFactory : public osg::Referenced
+namespace osgEarth_engine_osgterrain
 {
-public:
-    virtual osg::Node* createRootNode( const TileKey& key ) =0;
+    using namespace osgEarth;
+
+    /**
+     * A scene graph node that holds a terrain tile (as the single child of its
+     * osg::Group) and can load subtiles when they come into LOD range.
+     */
+    class KeyNodeFactory : public osg::Referenced
+    {
+    public:
+        virtual osg::Node* createRootNode( const TileKey& key ) =0;
+
+        virtual osg::Node* createNode( const TileKey& key ) =0;
 
-    virtual osg::Node* createNode( const TileKey& key ) =0;
+    protected:
+        KeyNodeFactory();
 
-protected:
-    KeyNodeFactory();
+        /** dtor */
+        virtual ~KeyNodeFactory() { }
+    };
 
-    /** dtor */
-    virtual ~KeyNodeFactory() { }
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_QUAD_TILE_NODE
diff --git a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
index 8ab3fda..4de5916 100644
--- a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
@@ -18,6 +18,7 @@
 */
 #include "KeyNodeFactory"
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 
 #define LC "[KeyNodeFactory] "
diff --git a/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback b/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback
index 0fdbbe2..929272f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback
+++ b/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback
@@ -22,14 +22,13 @@
 
 #include <osg/NodeCallback>
 
-namespace osgEarth
+namespace osgEarth_engine_osgterrain
 {
-    namespace Drivers
+    struct LODFactorCallback : public osg::NodeCallback
     {
-        struct LODFactorCallback : public osg::NodeCallback
-        {
-            void operator()(osg::Node* node, osg::NodeVisitor* nv);
-        };
-    }
-}
+        void operator()(osg::Node* node, osg::NodeVisitor* nv);
+    };
+
+} // namespace osgEarth_engine_osgterrain
+
 #endif
diff --git a/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp b/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp
index 78923fa..4bbffb5 100644
--- a/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp
@@ -24,62 +24,58 @@
 #include <osg/Uniform>
 #include <osgUtil/CullVisitor>
 
-namespace osgEarth
+using namespace osgEarth_engine_osgterrain;
+
+// This callback sets a uniform, osgearth_LODRangeFactor, based on the
+// distance from the camera and its relation to the minimum and
+// maximum distance for a tile. The maximum distance isn't actually
+// available, so 2 * min distance is used as an estimate. The range
+// factor's value goes from 0 - at the maximum range - to 1 for the
+// minimum range.
+
+void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
-    namespace Drivers
+    // test the type since this is not always a PagedLOD.
+    osg::PagedLOD* lod = static_cast<osg::PagedLOD*>(node);
+    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
+    osg::LOD::RangeMode rangeMode = lod->getRangeMode();
+    float requiredRange = 0.0f;
+    float rangeFactor = 1.0f;
+    const osg::LOD::RangeList& rangeList = lod->getRangeList();
+    if (rangeMode == osg::LOD::DISTANCE_FROM_EYE_POINT)
     {
-        // This callback sets a uniform, osgearth_LODRangeFactor, based on the
-        // distance from the camera and its relation to the minimum and
-        // maximum distance for a tile. The maximum distance isn't actually
-        // available, so 2 * min distance is used as an estimate. The range
-        // factor's value goes from 0 - at the maximum range - to 1 for the
-        // minimum range.
-
-        void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
+        requiredRange = cv->getDistanceToViewPoint(lod->getCenter(), true);
+    }
+    else if (cv->getLODScale() > 0.0f)
+    {
+        requiredRange = cv->clampedPixelSize(lod->getBound()) / cv->getLODScale();
+    }
+    else
+    {
+        // The comment in osg/PagedLOD.cpp says that this algorithm
+        // finds the highest res tile, but it actually finds the
+        // lowest res tile!
+        for (osg::LOD::RangeList::const_iterator itr = rangeList.begin(), end = rangeList.end();
+            itr != end;
+            ++itr)
         {
-            // test the type since this is not always a PagedLOD.
-            osg::PagedLOD* lod = static_cast<osg::PagedLOD*>(node);
-            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
-            osg::LOD::RangeMode rangeMode = lod->getRangeMode();
-            float requiredRange = 0.0f;
-            float rangeFactor = 1.0f;
-            const osg::LOD::RangeList& rangeList = lod->getRangeList();
-            if (rangeMode == osg::LOD::DISTANCE_FROM_EYE_POINT)
-            {
-                requiredRange = cv->getDistanceToViewPoint(lod->getCenter(), true);
-            }
-            else if (cv->getLODScale() > 0.0f)
-            {
-                requiredRange = cv->clampedPixelSize(lod->getBound()) / cv->getLODScale();
-            }
-            else
-            {
-                // The comment in osg/PagedLOD.cpp says that this algorithm
-                // finds the highest res tile, but it actually finds the
-                // lowest res tile!
-                for (osg::LOD::RangeList::const_iterator itr = rangeList.begin(), end = rangeList.end();
-                    itr != end;
-                    ++itr)
-                {
-                    requiredRange = osg::maximum(requiredRange, itr->first);
-                }
-            }
-            // We're counting on only finding one valid LOD, unlike the
-            // general OSG behavior.
-            if (!rangeList.empty() && rangeList[0].first <= requiredRange
-                && requiredRange < rangeList[0].second)
-            {
-                rangeFactor = 1.0f - (requiredRange - rangeList[0].first) / rangeList[0].first;
-                rangeFactor = osg::clampTo(rangeFactor, 0.0f, 1.0f);
-            }
-            osg::ref_ptr<osg::Uniform> ufact
-                = new osg::Uniform("osgearth_LODRangeFactor", rangeFactor);
-            osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
-            ss->addUniform(ufact.get());
-
-            cv->pushStateSet(ss.get());
-            traverse(node, nv);
-            cv->popStateSet();
+            requiredRange = osg::maximum(requiredRange, itr->first);
         }
     }
+    // We're counting on only finding one valid LOD, unlike the
+    // general OSG behavior.
+    if (!rangeList.empty() && rangeList[0].first <= requiredRange
+        && requiredRange < rangeList[0].second)
+    {
+        rangeFactor = 1.0f - (requiredRange - rangeList[0].first) / rangeList[0].first;
+        rangeFactor = osg::clampTo(rangeFactor, 0.0f, 1.0f);
+    }
+    osg::ref_ptr<osg::Uniform> ufact
+        = new osg::Uniform("osgearth_LODRangeFactor", rangeFactor);
+    osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
+    ss->addUniform(ufact.get());
+
+    cv->pushStateSet(ss.get());
+    traverse(node, nv);
+    cv->popStateSet();
 }
diff --git a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique
index a49cc7f..8d4e190 100644
--- a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique
+++ b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique
@@ -32,50 +32,53 @@
 
 #include <osgEarth/TextureCompositor>
 
-
-class MultiPassTerrainTechnique : public TerrainTechnique
+namespace osgEarth_engine_osgterrain
 {
-public:
+    class MultiPassTerrainTechnique : public TerrainTechnique
+    {
+    public:
+
+        MultiPassTerrainTechnique( TextureCompositor* texCompositor =0L );
 
-    MultiPassTerrainTechnique( TextureCompositor* texCompositor =0L );
+        /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
+        MultiPassTerrainTechnique(const MultiPassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
+        
+        META_Object( osgEarth, MultiPassTerrainTechnique );
 
-    /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
-    MultiPassTerrainTechnique(const MultiPassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
-    
-    META_Object( osgEarth, MultiPassTerrainTechnique );
+        virtual void init();
+        
+        virtual osgTerrain::Locator* computeMasterLocator();
+        
+        virtual osg::Vec3d computeCenterModel(osgTerrain::Locator* masterLocator);
+        
+        virtual void generateGeometry(osgTerrain::Locator* masterLocator, const osg::Vec3d& centerModel);     
 
-    virtual void init();
-    
-    virtual osgTerrain::Locator* computeMasterLocator();
-    
-    virtual osg::Vec3d computeCenterModel(osgTerrain::Locator* masterLocator);
-    
-    virtual void generateGeometry(osgTerrain::Locator* masterLocator, const osg::Vec3d& centerModel);     
+	    virtual osg::Geometry* createGeometryPrototype(osgTerrain::Locator* masterLocator, const osg::Vec3d& centerModel);
 
-	virtual osg::Geometry* createGeometryPrototype(osgTerrain::Locator* masterLocator, const osg::Vec3d& centerModel);
+	    osg::Geode* createPass(unsigned int order, const CustomColorLayer* layer, osgTerrain::Locator* masterLocator, const osg::Vec3d& centerModel, osg::Geometry* geometry); 
 
-	osg::Geode* createPass(unsigned int order, const CustomColorLayer* layer, osgTerrain::Locator* masterLocator, const osg::Vec3d& centerModel, osg::Geometry* geometry); 
+        /** Traverse the terain subgraph.*/
+        virtual void traverse(osg::NodeVisitor& nv);
 
-    /** Traverse the terain subgraph.*/
-    virtual void traverse(osg::NodeVisitor& nv);
+        /** If State is non-zero, this function releases any associated OpenGL objects for
+        * the specified graphics context. Otherwise, releases OpenGL objects
+        * for all graphics contexts. */
+        virtual void releaseGLObjects(osg::State* = 0) const;
 
-    /** If State is non-zero, this function releases any associated OpenGL objects for
-    * the specified graphics context. Otherwise, releases OpenGL objects
-    * for all graphics contexts. */
-    virtual void releaseGLObjects(osg::State* = 0) const;
 
+    private:
 
-private:
+        void updateTransparency();
+        void updateGeometry();
 
-    void updateTransparency();
-    void updateGeometry();
+        virtual ~MultiPassTerrainTechnique();
 
-    virtual ~MultiPassTerrainTechnique();
+        osg::ref_ptr<osg::MatrixTransform>  _transform;
+        osg::ref_ptr<osg::Group>            _passes;            
+        bool                                _terrainTileInitialized;
+        osg::ref_ptr<TextureCompositor>     _texCompositor;
+    };
 
-    osg::ref_ptr<osg::MatrixTransform>  _transform;
-    osg::ref_ptr<osg::Group>            _passes;            
-    bool                                _terrainTileInitialized;
-    osg::ref_ptr<TextureCompositor>     _texCompositor;
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif //OSGEARTH_ENGINE_OSGTERRAIN_MULTIPASS_TERRAIN_TECHNIQUE
diff --git a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp
index 4bdbb5a..1e1b313 100644
--- a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp
@@ -34,6 +34,7 @@
 #include <osg/Depth>
 #include <osg/Version>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 
 #define NEW_COORD_CODE
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode
index 880a323..5e8f33c 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode
@@ -32,106 +32,110 @@
 #include <osg/Geode>
 #include <osg/NodeCallback>
 
-using namespace osgEarth;
-
-class OSGTerrainEngineNode : public TerrainEngineNode
+namespace osgEarth_engine_osgterrain
 {
-public:
-    OSGTerrainEngineNode();
-    META_Node(osgEarth,OSGTerrainEngineNode);
-    virtual ~OSGTerrainEngineNode();
-
-public:
-    osg::Node* createNode(const TileKey& key);
-
-public: // TerrainEngineNode overrides    
-    virtual void preInitialize( const Map* map, const TerrainOptions& options );
-    virtual void postInitialize( const Map* map, const TerrainOptions& options );
-    virtual void validateTerrainOptions( TerrainOptions& options );
-    virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
-    virtual void traverse( osg::NodeVisitor& );
-    virtual osg::BoundingSphere computeBound() const;    
-
-    // for standalone tile creation outside of a terrain
-    osg::Node* createTile(const TileKey& key);
-
-public: // MapCallback adapter functions
-    void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
-    void onMapModelChanged( const MapModelChange& change ); // not virtual!
-
-    UID getUID() const;
-    OSGTileFactory* getTileFactory() const { return _tileFactory.get(); }
-    class TerrainNode* getTerrainNode() const { return _terrain; }
-
-public: // statics    
-    static void registerEngine( OSGTerrainEngineNode* engineNode );
-    static void unregisterEngine( UID uid );
-    static void getEngineByUID( UID uid, osg::ref_ptr<OSGTerrainEngineNode>& output );
-
-public:
-    class ElevationChangedCallback : public ElevationLayerCallback
+    using namespace osgEarth;
+
+    class OSGTerrainEngineNode : public TerrainEngineNode
     {
     public:
-        ElevationChangedCallback( OSGTerrainEngineNode* terrain );
+        OSGTerrainEngineNode();
+        META_Node(osgEarth,OSGTerrainEngineNode);
+        virtual ~OSGTerrainEngineNode();
 
-       virtual void onVisibleChanged( TerrainLayer* layer );
+    public:
+        osg::Node* createNode(const TileKey& key);
 
-        OSGTerrainEngineNode* _terrain;
-        friend class OSGTerrainEngineNode;
-    };
+    public: // TerrainEngineNode overrides    
+        virtual void preInitialize( const Map* map, const TerrainOptions& options );
+        virtual void postInitialize( const Map* map, const TerrainOptions& options );
+        virtual void validateTerrainOptions( TerrainOptions& options );
+        virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
+        virtual void traverse( osg::NodeVisitor& );
+        virtual osg::BoundingSphere computeBound() const;    
+
+        // for standalone tile creation outside of a terrain
+        osg::Node* createTile(const TileKey& key);
+
+    public: // MapCallback adapter functions
+        void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
+        void onMapModelChanged( const MapModelChange& change ); // not virtual!
+
+        UID getUID() const;
+        OSGTileFactory* getTileFactory() const { return _tileFactory.get(); }
+        class TerrainNode* getTerrainNode() const { return _terrain; }
 
-protected:
-	virtual void onVerticalScaleChanged();
+    public: // statics    
+        static void registerEngine( OSGTerrainEngineNode* engineNode );
+        static void unregisterEngine( UID uid );
+        static void getEngineByUID( UID uid, osg::ref_ptr<OSGTerrainEngineNode>& output );
 
-private:
-    void init();
-    void syncMapModel();
-    void installTerrainTechnique();
+    public:
+        class ElevationChangedCallback : public ElevationLayerCallback
+        {
+        public:
+            ElevationChangedCallback( OSGTerrainEngineNode* terrain );
+
+           virtual void onVisibleChanged( TerrainLayer* layer );
+
+            OSGTerrainEngineNode* _terrain;
+            friend class OSGTerrainEngineNode;
+        };
+
+    protected:
+	    virtual void onVerticalScaleChanged();
 
-    /**
-     * Reloads all the tiles in the terrain due to a data model change
-     */
-    void refresh();
+    private:
+        void init();
+        void syncMapModel();
+        void installTerrainTechnique();
 
+        /**
+         * Reloads all the tiles in the terrain due to a data model change
+         */
+        void refresh();
 
-    void addImageLayer( ImageLayer* layer );
-    void addElevationLayer( ElevationLayer* layer );
 
-    void removeImageLayer( ImageLayer* layerRemoved );
-    void removeElevationLayer( ElevationLayer* layerRemoved );
+        void addImageLayer( ImageLayer* layer );
+        void addElevationLayer( ElevationLayer* layer );
 
-    void moveImageLayer( unsigned int oldIndex, unsigned int newIndex );
-    void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex );
-    
-    void updateElevation( Tile* tile );
-    void installShaders();
-    void updateTextureCombining();
+        void removeImageLayer( ImageLayer* layerRemoved );
+        void removeElevationLayer( ElevationLayer* layerRemoved );
 
-private:
-    osg::ref_ptr<OSGTileFactory>         _tileFactory;
-    //class CustomTerrainNode* _terrain;
-    class TerrainNode*               _terrain;
-    UID                                  _uid;
-    osgEarth::Drivers::OSGTerrainOptions _terrainOptions;
-    Revision                             _shaderLibRev;
-    osg::ref_ptr<TaskServiceManager>     _taskServiceMgr;
+        void moveImageLayer( unsigned int oldIndex, unsigned int newIndex );
+        void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex );
+        
+        void updateElevation( Tile* tile );
+        void installShaders();
+        void updateTextureCombining();
 
-    osg::ref_ptr< ElevationChangedCallback > _elevationCallback;
+    private:
+        osg::ref_ptr<OSGTileFactory>         _tileFactory;
+        //class CustomTerrainNode* _terrain;
+        class TerrainNode*               _terrain;
+        UID                                  _uid;
+        osgEarth::Drivers::OSGTerrainOptions _terrainOptions;
+        Revision                             _shaderLibRev;
+        osg::ref_ptr<TaskServiceManager>     _taskServiceMgr;
 
-    // store a separate map frame for each of the traversal threads
-    MapFrame* _update_mapf; // map frame for the main/update traversal thread
-    MapFrame* _cull_mapf;   // map frame for the cull traversal thread
+        osg::ref_ptr< ElevationChangedCallback > _elevationCallback;
 
-    osg::ref_ptr<TaskService>    _tileService;
-    osg::ref_ptr<KeyNodeFactory> _keyNodeFactory;
-    osg::ref_ptr<TileBuilder>    _tileBuilder;
+        // store a separate map frame for each of the traversal threads
+        MapFrame* _update_mapf; // map frame for the main/update traversal thread
+        MapFrame* _cull_mapf;   // map frame for the cull traversal thread
 
-    osg::Timer _timer;
-    unsigned   _tileCount;
-    double     _tileCreationTime;
-    bool       _isStreaming;
+        osg::ref_ptr<TaskService>    _tileService;
+        osg::ref_ptr<KeyNodeFactory> _keyNodeFactory;
+        osg::ref_ptr<TileBuilder>    _tileBuilder;
+
+        osg::Timer _timer;
+        unsigned   _tileCount;
+        double     _tileCreationTime;
+        bool       _isStreaming;
+
+        OSGTerrainEngineNode( const OSGTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
+    };
 
-    OSGTerrainEngineNode( const OSGTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_ENGINE_NODE_H
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp
index 6eb4353..c347610 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp
@@ -27,7 +27,9 @@
 
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/ShaderFactory>
+#include <osgEarth/MapModelChange>
 #include <osg/TexEnv>
 #include <osg/TexEnvCombine>
 #include <osg/PagedLOD>
@@ -35,6 +37,7 @@
 
 #define LC "[OSGTerrainEngine] "
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 
 //------------------------------------------------------------------------
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory
index 12227b4..73ff184 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory
@@ -34,140 +34,144 @@
 #include <osg/ClusterCullingCallback>
 #include <vector>
 
-using namespace osgEarth;
-using namespace osgEarth::Drivers;
-
-class TerrainNode;
-class StreamingTerrainNode;
-class Tile;
-class StreamingTile;
-
-/**
-* TileFactory is the main workhorse - it generates osg Nodes for TileKeys.
-*/
-class OSGTileFactory : public osg::Referenced
+namespace osgEarth_engine_osgterrain
 {
-public:
-    OSGTileFactory(
-        unsigned                 engineId,
-        const MapFrame&          cull_thread_mapf,
-        const OSGTerrainOptions& props =OSGTerrainOptions() );
-
-    /** dtor */
-    virtual ~OSGTileFactory() { }
-
-public:
-    /**
-    * Creates a node graph containing four tiles that correspond to the four
-    * subkeys of the provided TileKey.
-    */
-    osg::Node* createSubTiles(
-        const MapFrame&  mapf,
-        TerrainNode*     terrain,
-        const TileKey&   key,
-        bool             populateLayers );
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
 
-    /**
-    * Creates a single terrain tile corresponding to the provided TileKey.
-    */
-    osg::Node* createTile(
-        const MapFrame&  mapf,
-        TerrainNode*     terrain,
-        const TileKey&   key,
-        bool             populateLayers,
-        bool             wrapInPagedLOD,
-        bool             fallback,
-        bool&            out_validData);
-
-
-    CustomColorLayerRef* createImageLayer(
-        const MapInfo&    mapInfo,
-        ImageLayer*       layer,
-        const TileKey&    key,
-        ProgressCallback* progress);
-
-    osgTerrain::HeightFieldLayer* createHeightFieldLayer(
-        const MapFrame& mapf,
-        const TileKey&  key,
-        bool            exactOnly );
+    class TerrainNode;
+    class StreamingTerrainNode;
+    class Tile;
+    class StreamingTile;
 
     /**
-    * Gets the properties that customize how this engine renders tile data.
+    * TileFactory is the main workhorse - it generates osg Nodes for TileKeys.
     */
-    const OSGTerrainOptions& getTerrainOptions() const;
-
-    /**
-    * Gets a pagedLOD child URI given a tile key.
-    */
-    std::string createURI( unsigned int id, const TileKey& key );
-
-    /**
-     * Wraps a tile in a paged LOD and setup up all its parameters
-     */
-    osg::Node* prepareTile( 
-        Tile*            tile, 
-        TerrainNode*         terrain,
-        const MapInfo&   mapInfo,
-        bool             wrapInPagedLOD );
-
-    bool createValidGeoImage(
-        ImageLayer*       layer,
-        const TileKey&    key,
-        GeoImage&         out_image,
-        TileKey&          out_actualTileKey,
-        ProgressCallback* progress  = 0);
-
-    osg::Matrixd getTransformFromExtents(double minX, double minY, double maxX, double maxY) const;
-
-    // checks whether more data exists below the specified key's level of detail
-    bool hasMoreLevels( Map* map, const TileKey& key );
-
-    static osg::HeightField* createEmptyHeightField(
-        const TileKey& key,
-        unsigned       numCols =8,
-        unsigned       numRows =8 );
-
-    osgTerrain::HeightFieldLayer* createPlaceholderHeightfieldLayer(
-        osg::HeightField* ancestorHF,
-        const TileKey&    ancestorKey,
-        const TileKey&    key,
-        GeoLocator*       locator );
-
-protected:
-
-    osg::Node* createPlaceholderTile(
-        const MapFrame&   mapf,
-        StreamingTerrainNode* terrain,
-        const TileKey&    key );
-
-    osg::Node* createPopulatedTile(
-        const MapFrame&  mapf,
-        TerrainNode*         terrain,
-        const TileKey&   key,
-        bool             wrapInPagedLOD,
-        bool             fallback,
-        bool&            out_validData);
-
-    void addPlaceholderImageLayers( Tile* tile, Tile* ancestorTile );
-
-    void addPlaceholderHeightfieldLayer(
-        StreamingTile* tile,
-        StreamingTile* ancestorTile,
-        GeoLocator*    defaultLocator,
-        const TileKey& key,
-        const TileKey& ancestorKey );
-
-    osg::ClusterCullingCallback* createClusterCullingCallback(
-        Tile*                tile,
-        osg::EllipsoidModel* et );
-
-    void init();
-
-protected:
-
-    unsigned          _engineId;
-    const MapFrame&   _cull_thread_mapf;
-    OSGTerrainOptions _terrainOptions;
-};
+    class OSGTileFactory : public osg::Referenced
+    {
+    public:
+        OSGTileFactory(
+            unsigned                 engineId,
+            const MapFrame&          cull_thread_mapf,
+            const OSGTerrainOptions& props =OSGTerrainOptions() );
+
+        /** dtor */
+        virtual ~OSGTileFactory() { }
+
+    public:
+        /**
+        * Creates a node graph containing four tiles that correspond to the four
+        * subkeys of the provided TileKey.
+        */
+        osg::Node* createSubTiles(
+            const MapFrame&  mapf,
+            TerrainNode*     terrain,
+            const TileKey&   key,
+            bool             populateLayers );
+
+        /**
+        * Creates a single terrain tile corresponding to the provided TileKey.
+        */
+        osg::Node* createTile(
+            const MapFrame&  mapf,
+            TerrainNode*     terrain,
+            const TileKey&   key,
+            bool             populateLayers,
+            bool             wrapInPagedLOD,
+            bool             fallback,
+            bool&            out_validData);
+
+
+        CustomColorLayerRef* createImageLayer(
+            const MapInfo&    mapInfo,
+            ImageLayer*       layer,
+            const TileKey&    key,
+            ProgressCallback* progress);
+
+        osgTerrain::HeightFieldLayer* createHeightFieldLayer(
+            const MapFrame& mapf,
+            const TileKey&  key,
+            bool            exactOnly );
+
+        /**
+        * Gets the properties that customize how this engine renders tile data.
+        */
+        const OSGTerrainOptions& getTerrainOptions() const;
+
+        /**
+        * Gets a pagedLOD child URI given a tile key.
+        */
+        std::string createURI( unsigned int id, const TileKey& key );
+
+        /**
+         * Wraps a tile in a paged LOD and setup up all its parameters
+         */
+        osg::Node* prepareTile( 
+            Tile*            tile, 
+            TerrainNode*         terrain,
+            const MapInfo&   mapInfo,
+            bool             wrapInPagedLOD );
+
+        bool createValidGeoImage(
+            ImageLayer*       layer,
+            const TileKey&    key,
+            GeoImage&         out_image,
+            TileKey&          out_actualTileKey,
+            ProgressCallback* progress  = 0);
+
+        osg::Matrixd getTransformFromExtents(double minX, double minY, double maxX, double maxY) const;
+
+        // checks whether more data exists below the specified key's level of detail
+        bool hasMoreLevels( Map* map, const TileKey& key );
+
+        static osg::HeightField* createEmptyHeightField(
+            const TileKey& key,
+            unsigned       numCols =8,
+            unsigned       numRows =8 );
+
+        osgTerrain::HeightFieldLayer* createPlaceholderHeightfieldLayer(
+            osg::HeightField* ancestorHF,
+            const TileKey&    ancestorKey,
+            const TileKey&    key,
+            GeoLocator*       locator );
+
+    protected:
+
+        osg::Node* createPlaceholderTile(
+            const MapFrame&   mapf,
+            StreamingTerrainNode* terrain,
+            const TileKey&    key );
+
+        osg::Node* createPopulatedTile(
+            const MapFrame&  mapf,
+            TerrainNode*         terrain,
+            const TileKey&   key,
+            bool             wrapInPagedLOD,
+            bool             fallback,
+            bool&            out_validData);
+
+        void addPlaceholderImageLayers( Tile* tile, Tile* ancestorTile );
+
+        void addPlaceholderHeightfieldLayer(
+            StreamingTile* tile,
+            StreamingTile* ancestorTile,
+            GeoLocator*    defaultLocator,
+            const TileKey& key,
+            const TileKey& ancestorKey );
+
+        osg::ClusterCullingCallback* createClusterCullingCallback(
+            Tile*                tile,
+            osg::EllipsoidModel* et );
+
+        void init();
+
+    protected:
+
+        unsigned          _engineId;
+        const MapFrame&   _cull_thread_mapf;
+        OSGTerrainOptions _terrainOptions;
+    };
+
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_TILE_FACTORY_H
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp
index a8c2390..e6c3e95 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp
@@ -43,6 +43,7 @@
 #include <stdlib.h>
 
 using namespace OpenThreads;
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 
@@ -418,16 +419,7 @@ OSGTileFactory::createPlaceholderTile(const MapFrame&   mapf,
     tile->setTerrainTechnique( terrain->cloneTechnique() );
     tile->setVerticalScale( _terrainOptions.verticalScale().value() );
     tile->setDataVariance( osg::Object::DYNAMIC );
-    //tile->setLocator( locator.get() );
-
-    // Attach an updatecallback to normalize the edges of TerrainTiles.
-#if 0
-    if ( hasElevation && _terrainOptions.normalizeEdges().get() )
-    {
-        tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
-        tile->setDataVariance(osg::Object::DYNAMIC);
-    }
-#endif
+    //tile->setLocator( locator.get() );    
 
     // Generate placeholder imagery and elevation layers. These "inherit" data from an
     // ancestor tile.
@@ -665,15 +657,6 @@ OSGTileFactory::createPopulatedTile(const MapFrame&  mapf,
     //tile->setRequiresNormals( true );
     tile->setDataVariance(osg::Object::DYNAMIC);
 
-#if 0
-    //Attach an updatecallback to normalize the edges of TerrainTiles.
-    if (hasElevation && _terrainOptions.normalizeEdges().get() )
-    {
-        tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
-        tile->setDataVariance(osg::Object::DYNAMIC);
-    }
-#endif
-
     //Assign the terrain system to the TerrainTile.
     //It is very important the terrain system is set while the MapConfig's sourceMutex is locked.
     //This registers the terrain tile so that adding/removing layers are always in sync.  If you don't do this
diff --git a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory
index 5bd1efe..d152fae 100644
--- a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory
@@ -22,23 +22,27 @@
 #include "Common"
 #include "SerialKeyNodeFactory"
 
-using namespace osgEarth;
-
-class ParallelKeyNodeFactory : public SerialKeyNodeFactory
+namespace osgEarth_engine_osgterrain
 {
-public:
-    ParallelKeyNodeFactory(
-        TileBuilder*             builder,
-        const OSGTerrainOptions& options,
-        const MapInfo&           mapInfo,
-        TerrainNode*             terrain,
-        UID                      engineUID );
+    using namespace osgEarth;
+
+    class ParallelKeyNodeFactory : public SerialKeyNodeFactory
+    {
+    public:
+        ParallelKeyNodeFactory(
+            TileBuilder*             builder,
+            const OSGTerrainOptions& options,
+            const MapInfo&           mapInfo,
+            TerrainNode*             terrain,
+            UID                      engineUID );
+
+        /** dtor */
+        virtual ~ParallelKeyNodeFactory() { }
 
-    /** dtor */
-    virtual ~ParallelKeyNodeFactory() { }
+        osg::Node* createRootNode( const TileKey& key );
+        osg::Node* createNode( const TileKey& key );
+    };
 
-    osg::Node* createRootNode( const TileKey& key );
-    osg::Node* createNode( const TileKey& key );
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_PARALLEL_KEY_NODE_FACTORY
diff --git a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp
index 2911a91..07f599f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp
@@ -20,6 +20,7 @@
 #include <osgEarth/Registry>
 #include <osg/PagedLOD>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp b/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp
index 3a5b4b5..ef095c1 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp
@@ -26,12 +26,13 @@
 
 #define LC "[osgterrain_engine Plugin] "
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth::Drivers;
 
-class OSGTerrainEnginePlugin : public osgDB::ReaderWriter
+class osgEarth_OSGTerrainEnginePlugin : public osgDB::ReaderWriter
 {
 public:
-    OSGTerrainEnginePlugin() {}
+    osgEarth_OSGTerrainEnginePlugin() {}
 
     virtual const char* className()
     {
@@ -136,4 +137,4 @@ public:
     }
 };
 
-REGISTER_OSGPLUGIN(osgearth_engine_osgterrain, OSGTerrainEnginePlugin)
+REGISTER_OSGPLUGIN(osgearth_engine_osgterrain, osgEarth_OSGTerrainEnginePlugin)
diff --git a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory
index 01dc3fa..7fc1725 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory
@@ -24,32 +24,36 @@
 #include "TerrainNode"
 #include "TileBuilder"
 
-using namespace osgEarth;
-
-class SerialKeyNodeFactory : public KeyNodeFactory
+namespace osgEarth_engine_osgterrain
 {
-public:
-    SerialKeyNodeFactory(
-        TileBuilder*             builder,
-        const OSGTerrainOptions& options,
-        const MapInfo&           mapInfo,
-        TerrainNode*             terrain,
-        UID                      engineUID );
-
-    /** dtor */
-    virtual ~SerialKeyNodeFactory() { }
-
-    osg::Node* createRootNode( const TileKey& key );
-    osg::Node* createNode( const TileKey& key );
-
-protected:
-    void addTile(Tile* tile, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent );
-
-    osg::ref_ptr< TileBuilder> _builder;
-    const OSGTerrainOptions& _options;
-    const MapInfo            _mapInfo;
-    osg::ref_ptr< TerrainNode > _terrain;
-    UID                      _engineUID;
-};
+    using namespace osgEarth;
+
+    class SerialKeyNodeFactory : public KeyNodeFactory
+    {
+    public:
+        SerialKeyNodeFactory(
+            TileBuilder*             builder,
+            const OSGTerrainOptions& options,
+            const MapInfo&           mapInfo,
+            TerrainNode*             terrain,
+            UID                      engineUID );
+
+        /** dtor */
+        virtual ~SerialKeyNodeFactory() { }
+
+        osg::Node* createRootNode( const TileKey& key );
+        osg::Node* createNode( const TileKey& key );
+
+    protected:
+        void addTile(Tile* tile, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent );
+
+        osg::ref_ptr< TileBuilder> _builder;
+        const OSGTerrainOptions& _options;
+        const MapInfo            _mapInfo;
+        osg::ref_ptr< TerrainNode > _terrain;
+        UID                      _engineUID;
+    };
+
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_PARALLEL_KEY_NODE_FACTORY
diff --git a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp
index f9014f6..62437a7 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp
@@ -28,6 +28,7 @@
 
 #include <osgEarth/MapNode>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
@@ -117,7 +118,7 @@ SerialKeyNodeFactory::addTile(Tile* tile, bool tileHasRealData, bool tileHasLodB
         {
             // Make the LOD transition distance, and a measure of how
             // close the tile is to an LOD change, to shaders.
-            result->addCullCallback(new Drivers::LODFactorCallback);
+            result->addCullCallback(new LODFactorCallback);
         }
     }
     else
diff --git a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique
index 73172e3..61ee27f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique
+++ b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique
@@ -41,158 +41,162 @@
 
 #include <queue>
 
-class Tile;
-class TileFrame;
-
-// --------------------------------------------------------------------------
-
-/**
- * A terrain technique that uses a single texture unit by compositing image layer textures
- * on the GPU.
- * 
- * This technique works by creating a single "mosaic" texture and copying each image layer's 
- * texture into that mosaic. It then creates a uniform array that conveys the relative offset
- * and scale information of each sub-texture to a shader. The shader then composites the
- * approprate mosaic texels on the GPU.
- *
- * Limitations:
- *
- * This technique is limited by the maximum texture size your GPU will support, since it 
- * creates a single mosaic texture. For example, if your GPU's max texture size is 2048,
- * this technique can support 64 256-pixel layers.
- */
-class SinglePassTerrainTechnique : public CustomTerrainTechnique
+namespace osgEarth_engine_osgterrain
 {
-public:
-    SinglePassTerrainTechnique( TextureCompositor* compositor =0L ); //osgTerrain::Locator* locator =0L );
+    class Tile;
+    class TileFrame;
 
-    SinglePassTerrainTechnique(const SinglePassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
-
-    META_Object( osgEarth, SinglePassTerrainTechnique );
-
-    /** dtor */
-    virtual ~SinglePassTerrainTechnique();
-
-public: /* overrides */
-
-    virtual void init();
-
-#if 0
-#if OSG_MIN_VERSION_REQUIRED(2,9,8)        
-    virtual void init(int dirtyMask, bool assumeMultiThreaded);
-#else
-    virtual void init();
-#endif
-#endif
-
-    void setParentTile( Tile* tile ) { _parentTile = tile; }
-
-    void compile( const TileUpdate& updateSpec, ProgressCallback* progress );
-
-    // returns TRUE if a swap occurred and a new subgraph is now in place.
-    bool applyTileUpdates();
-
-    /** Traverse the terrain subgraph.*/
-    virtual void traverse( osg::NodeVisitor& nv );
-
-public:
-
-    /**
-    * Sets a factor by which to scale elevation height values. By default, this object
-    * will get the vertical scale from the osgTerrain::Terrain with which the tile is
-    * associated. Setting this value overrides that (or sets it if there is no terrain).
-    */
-    void setVerticalScaleOverride( float value );
+    // --------------------------------------------------------------------------
 
     /**
-    * Gets the overriden vertical scale value.
-    */
-    float getVerticalScaleOverride() const;
-
-    /**
-     * Sets whether to try to optimize the triangle orientation based on the elevation values.
-     *  If false, 
+     * A terrain technique that uses a single texture unit by compositing image layer textures
+     * on the GPU.
+     * 
+     * This technique works by creating a single "mosaic" texture and copying each image layer's 
+     * texture into that mosaic. It then creates a uniform array that conveys the relative offset
+     * and scale information of each sub-texture to a shader. The shader then composites the
+     * approprate mosaic texels on the GPU.
+     *
+     * Limitations:
+     *
+     * This technique is limited by the maximum texture size your GPU will support, since it 
+     * creates a single mosaic texture. For example, if your GPU's max texture size is 2048,
+     * this technique can support 64 256-pixel layers.
      */
-    void setOptimizeTriangleOrientation(bool optimizeTriangleOrientation);
-    bool getOptimizeTriangleOrientation() const;
+    class SinglePassTerrainTechnique : public CustomTerrainTechnique
+    {
+    public:
+        SinglePassTerrainTechnique( TextureCompositor* compositor =0L ); //osgTerrain::Locator* locator =0L );
 
-    /** If State is non-zero, this function releases any associated OpenGL objects for
-    * the specified graphics context. Otherwise, releases OpenGL objects
-    * for all graphics contexts. */
-    virtual void releaseGLObjects(osg::State* = 0) const;
+        SinglePassTerrainTechnique(const SinglePassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
 
-    osg::StateSet* getActiveStateSet() const;
+        META_Object( osgEarth, SinglePassTerrainTechnique );
 
-    /** Gets to the underlying transform that parents the actual constructed geometry. */
-    osg::Transform* getTransform() const { return _transform.get(); }
-    osg::Transform* takeTransform() { return _transform.release(); }
+        /** dtor */
+        virtual ~SinglePassTerrainTechnique();
 
-    bool getClearDataAfterCompile() const { return _clearDataAfterCompile;}
-    void setClearDataAfterCompile( bool value) { _clearDataAfterCompile = value;}
+    public: /* overrides */
 
-protected:
+        virtual void init();
 
-    void calculateSampling( unsigned int& out_rows, unsigned int& out_cols, double& out_i, double& out_j );
+    #if 0
+    #if OSG_MIN_VERSION_REQUIRED(2,9,8)        
+        virtual void init(int dirtyMask, bool assumeMultiThreaded);
+    #else
+        virtual void init();
+    #endif
+    #endif
 
-private:
-    bool _debug;
+        void setParentTile( Tile* tile ) { _parentTile = tile; }
 
-    OpenThreads::Mutex _compileMutex;
-    //OpenThreads::Mutex                  _writeBufferMutex;
-    osg::ref_ptr<osg::MatrixTransform> _transform;
-    osg::ref_ptr<osg::Group> _backNode;
-    osg::ref_ptr<osg::Uniform> _imageLayerStampUniform;
-    osg::Vec3d _centerModel;
-    float _verticalScaleOverride;
-    osg::ref_ptr<GeoLocator> _masterLocator;
+        void compile( const TileUpdate& updateSpec, ProgressCallback* progress );
+
+        // returns TRUE if a swap occurred and a new subgraph is now in place.
+        bool applyTileUpdates();
 
-    int  _initCount;
-    bool _pendingFullUpdate;    
-    bool _pendingGeometryUpdate;
-    bool _clearDataAfterCompile;
+        /** Traverse the terrain subgraph.*/
+        virtual void traverse( osg::NodeVisitor& nv );
+
+    public:
+
+        /**
+        * Sets a factor by which to scale elevation height values. By default, this object
+        * will get the vertical scale from the osgTerrain::Terrain with which the tile is
+        * associated. Setting this value overrides that (or sets it if there is no terrain).
+        */
+        void setVerticalScaleOverride( float value );
+
+        /**
+        * Gets the overriden vertical scale value.
+        */
+        float getVerticalScaleOverride() const;
+
+        /**
+         * Sets whether to try to optimize the triangle orientation based on the elevation values.
+         *  If false, 
+         */
+        void setOptimizeTriangleOrientation(bool optimizeTriangleOrientation);
+        bool getOptimizeTriangleOrientation() const;
+
+        /** If State is non-zero, this function releases any associated OpenGL objects for
+        * the specified graphics context. Otherwise, releases OpenGL objects
+        * for all graphics contexts. */
+        virtual void releaseGLObjects(osg::State* = 0) const;
+
+        osg::StateSet* getActiveStateSet() const;
+
+        /** Gets to the underlying transform that parents the actual constructed geometry. */
+        osg::Transform* getTransform() const { return _transform.get(); }
+        osg::Transform* takeTransform() { return _transform.release(); }
+
+        bool getClearDataAfterCompile() const { return _clearDataAfterCompile;}
+        void setClearDataAfterCompile( bool value) { _clearDataAfterCompile = value;}
+
+    protected:
+
+        void calculateSampling( unsigned int& out_rows, unsigned int& out_cols, double& out_i, double& out_j );
+
+    private:
+        bool _debug;
+
+        OpenThreads::Mutex _compileMutex;
+        //OpenThreads::Mutex                  _writeBufferMutex;
+        osg::ref_ptr<osg::MatrixTransform> _transform;
+        osg::ref_ptr<osg::Group> _backNode;
+        osg::ref_ptr<osg::Uniform> _imageLayerStampUniform;
+        osg::Vec3d _centerModel;
+        float _verticalScaleOverride;
+        osg::ref_ptr<GeoLocator> _masterLocator;
+
+        int  _initCount;
+        bool _pendingFullUpdate;    
+        bool _pendingGeometryUpdate;
+        bool _clearDataAfterCompile;
+
+        struct ImageLayerUpdate {
+            GeoImage _image;
+            UID      _layerUID;
+            bool     _isRealData; // versus fallback data
+        };
+        typedef std::queue<ImageLayerUpdate> ImageLayerUpdates;
+        ImageLayerUpdates _pendingImageLayerUpdates;
+
+        // associates each texture index with a layer UID.
+        typedef std::map< UID, int > LayerUIDtoIndexMap;
+        LayerUIDtoIndexMap _layerUIDtoIndexMap;
 
-    struct ImageLayerUpdate {
-        GeoImage _image;
-        UID      _layerUID;
-        bool     _isRealData; // versus fallback data
+        // XXX Are these both necessary?
+        GeoExtent _tileExtent;
+        TileKey _tileKey;
+
+        bool _optimizeTriangleOrientation;
+
+        osg::ref_ptr<const TextureCompositor> _texCompositor;
+        bool _frontGeodeInstalled;
+
+        OpenThreads::Atomic _atomicCallOnce;
+
+    private:
+
+        osg::Vec3d computeCenterModel();
+        bool createGeoImage( const CustomColorLayer& layer, GeoImage& image ) const; //const osgTerrain::Layer* imageLayer ) const;
+        osg::Group* createGeometry( const TileFrame& tilef );
+        osg::StateSet* createStateSet( const TileFrame& tilef );
+        void prepareImageLayerUpdate( int layerIndex, const TileFrame& tilef );
+        //Threading::ReadWriteMutex& getMutex();
+        inline osg::Group* getFrontNode() const {
+            if (_transform.valid() && _transform->getNumChildren() > 0)
+                return static_cast<osg::Group*>(_transform->getChild(0));
+            return NULL;
+        }
+        osg::StateSet* getParentStateSet() const;
+        //const CustomColorLayer* getLayerByUID( UID uid ) const;
+        int getIndexOfColorLayerWithUID( UID uid ) const;
+
+        
+        osg::observer_ptr<Tile> _parentTile;
     };
-    typedef std::queue<ImageLayerUpdate> ImageLayerUpdates;
-    ImageLayerUpdates _pendingImageLayerUpdates;
-
-    // associates each texture index with a layer UID.
-    typedef std::map< UID, int > LayerUIDtoIndexMap;
-    LayerUIDtoIndexMap _layerUIDtoIndexMap;
-
-    // XXX Are these both necessary?
-    GeoExtent _tileExtent;
-    TileKey _tileKey;
-
-    bool _optimizeTriangleOrientation;
-
-    osg::ref_ptr<const TextureCompositor> _texCompositor;
-    bool _frontGeodeInstalled;
-
-    OpenThreads::Atomic _atomicCallOnce;
-
-private:
-
-    osg::Vec3d computeCenterModel();
-    bool createGeoImage( const CustomColorLayer& layer, GeoImage& image ) const; //const osgTerrain::Layer* imageLayer ) const;
-    osg::Group* createGeometry( const TileFrame& tilef );
-    osg::StateSet* createStateSet( const TileFrame& tilef );
-    void prepareImageLayerUpdate( int layerIndex, const TileFrame& tilef );
-    //Threading::ReadWriteMutex& getMutex();
-    inline osg::Group* getFrontNode() const {
-        if (_transform.valid() && _transform->getNumChildren() > 0)
-            return static_cast<osg::Group*>(_transform->getChild(0));
-        return NULL;
-    }
-    osg::StateSet* getParentStateSet() const;
-    //const CustomColorLayer* getLayerByUID( UID uid ) const;
-    int getIndexOfColorLayerWithUID( UID uid ) const;
-
-    
-    osg::observer_ptr<Tile> _parentTile;
-};
+
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_SINGLE_PASS_TERRAIN_TECHNIQUE
diff --git a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp
index 9dc652b..19ffae3 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp
@@ -40,6 +40,7 @@
 
 #include <sstream>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
 using namespace OpenThreads;
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode
index 74ec20c..662b862 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode
@@ -23,65 +23,69 @@
 #include "StreamingTile"
 #include <osgEarth/TaskService>
 
-using namespace osgEarth;
-
-/**
- * Terrain implementation that supports the SEQUENTIAL and PREEMPTIVE loading policies.
- */
-class StreamingTerrainNode : public TerrainNode
+namespace osgEarth_engine_osgterrain
 {
-public:
-    StreamingTerrainNode(
-        const MapFrame& update_mapf,
-        const MapFrame& cull_mapf,
-        OSGTileFactory* factory,
-        bool            quickReleaseGLObjects );
+    using namespace osgEarth;
 
-    virtual const char* libraryName() const { return "osgEarth"; }
-    virtual const char* className() const { return "StreamingTerrainNode"; }
-    
-    //override
-    virtual Tile* createTile(const TileKey& key, GeoLocator* locator) const;
+    /**
+     * Terrain implementation that supports the SEQUENTIAL and PREEMPTIVE loading policies.
+     */
+    class StreamingTerrainNode : public TerrainNode
+    {
+    public:
+        StreamingTerrainNode(
+            const MapFrame& update_mapf,
+            const MapFrame& cull_mapf,
+            OSGTileFactory* factory,
+            bool            quickReleaseGLObjects );
 
-public:
+        virtual const char* libraryName() const { return "osgEarth"; }
+        virtual const char* className() const { return "StreamingTerrainNode"; }
+        
+        //override
+        virtual Tile* createTile(const TileKey& key, GeoLocator* locator) const;
 
-    TaskService* getImageryTaskService(int layerId);
-    TaskService* getElevationTaskService();
-    TaskService* getTileGenerationTaskService();
+    public:
 
-    /**
-     * Updates the catalog of task service threads - this gets called by the OSGTerrainEngine
-     * in response to a change in the Map's data model. The map frame is that of the terrain
-     * engine.
-     */
-    void updateTaskServiceThreads( const MapFrame& mapf );
+        TaskService* getImageryTaskService(int layerId);
+        TaskService* getElevationTaskService();
+        TaskService* getTileGenerationTaskService();
+
+        /**
+         * Updates the catalog of task service threads - this gets called by the OSGTerrainEngine
+         * in response to a change in the Map's data model. The map frame is that of the terrain
+         * engine.
+         */
+        void updateTaskServiceThreads( const MapFrame& mapf );
+
+        const LoadingPolicy& getLoadingPolicy() const { return _loadingPolicy; }
 
-    const LoadingPolicy& getLoadingPolicy() const { return _loadingPolicy; }
+    protected:
 
-protected:
+	    virtual ~StreamingTerrainNode();
 
-	virtual ~StreamingTerrainNode();
+        //override
+        virtual unsigned getNumActiveTasks() const;
 
-    //override
-    virtual unsigned getNumActiveTasks() const;
+        //override
+        virtual void updateTraversal( osg::NodeVisitor& nv );
 
-    //override
-    virtual void updateTraversal( osg::NodeVisitor& nv );
+    private:
 
-private:
+        TaskService* createTaskService( const std::string& name, int id, int numThreads );
+        TaskService* getTaskService( int id );
 
-    TaskService* createTaskService( const std::string& name, int id, int numThreads );
-    TaskService* getTaskService( int id );
+        void refreshFamily( const MapInfo& info, const TileKey& key, StreamingTile::Relative* family, bool tileTableLocked );
 
-    void refreshFamily( const MapInfo& info, const TileKey& key, StreamingTile::Relative* family, bool tileTableLocked );
+        typedef std::map< int, osg::ref_ptr< TaskService > > TaskServiceMap;
 
-    typedef std::map< int, osg::ref_ptr< TaskService > > TaskServiceMap;
+        TaskServiceMap     _taskServices;
+        OpenThreads::Mutex _taskServiceMutex;
+        int                _numLoadingThreads;
+        LoadingPolicy      _loadingPolicy;
+        UID                _elevationTaskServiceUID;
+    };
 
-    TaskServiceMap     _taskServices;
-    OpenThreads::Mutex _taskServiceMutex;
-    int                _numLoadingThreads;
-    LoadingPolicy      _loadingPolicy;
-    UID                _elevationTaskServiceUID;
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TERRAIN
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp
index d8af364..3dfb4aa 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp
@@ -21,7 +21,8 @@
 #include "TransparentLayer"
 
 #include <osgEarth/Registry>
-#include <osgEarth/Map>
+#include <osgEarth/MapFrame>
+#include <osgEarth/MapInfo>
 #include <osgEarth/NodeUtils>
 
 #include <osg/NodeCallback>
@@ -31,6 +32,7 @@
 
 #include <OpenThreads/ScopedLock>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTile b/src/osgEarthDrivers/engine_osgterrain/StreamingTile
index 203a25e..5eed3e5 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTile
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTile
@@ -22,124 +22,127 @@
 #include "Tile"
 #include <osgEarth/TaskService>
 
-class TileFactory;
-class StreamingTerrain;
+namespace osgEarth_engine_osgterrain
+{
+    class TileFactory;
+    class StreamingTerrain;
 
-using namespace osgEarth;
+    using namespace osgEarth;
 
-//------------------------------------------------------------------------
+    //------------------------------------------------------------------------
 
-class StreamingTile : public Tile
-{
-public:
-    // Please refer the StreamingTerrain::refreshFamily() method for more info on this structure.
-    struct Relative
+    class StreamingTile : public Tile
     {
-        int getImageLOD(unsigned int layerID)
+    public:
+        // Please refer the StreamingTerrain::refreshFamily() method for more info on this structure.
+        struct Relative
         {
-            LayerIDtoLODMap::iterator itr = imageLODs.find(layerID);
-            if (itr != imageLODs.end()) return itr->second;
-            return -1;
-        }
-        
-        typedef std::map<unsigned int, int> LayerIDtoLODMap;
-
-        bool               expected;
-        int                elevLOD;
-        LayerIDtoLODMap    imageLODs;
-        osgTerrain::TileID tileID;
-
-        enum Direction {
-            PARENT =0,
-            WEST   =1,
-            NORTH  =2,
-            EAST   =3,
-            SOUTH  =4
+            int getImageLOD(unsigned int layerID)
+            {
+                LayerIDtoLODMap::iterator itr = imageLODs.find(layerID);
+                if (itr != imageLODs.end()) return itr->second;
+                return -1;
+            }
+            
+            typedef std::map<unsigned int, int> LayerIDtoLODMap;
+
+            bool               expected;
+            int                elevLOD;
+            LayerIDtoLODMap    imageLODs;
+            osgTerrain::TileID tileID;
+
+            enum Direction {
+                PARENT =0,
+                WEST   =1,
+                NORTH  =2,
+                EAST   =3,
+                SOUTH  =4
+            };
+
+            Relative() { }
         };
 
-        Relative() { }
-    };
+    public:
+        StreamingTile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects );
 
-public:
-    StreamingTile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects );
+        virtual const char* libraryName() const { return "osgEarth"; }
+        virtual const char* className() const { return "StreamingTile"; }
 
-    virtual const char* libraryName() const { return "osgEarth"; }
-    virtual const char* className() const { return "StreamingTile"; }
+        // Updates and services this tile's image request tasks
+        void servicePendingImageRequests( const MapFrame& mapf, int stamp );
 
-    // Updates and services this tile's image request tasks
-    void servicePendingImageRequests( const MapFrame& mapf, int stamp );
+        // Updates and services this tile's heightfield request tasks
+        void servicePendingElevationRequests( const MapFrame& mapf, int stamp, bool tileTableLocked );
 
-    // Updates and services this tile's heightfield request tasks
-    void servicePendingElevationRequests( const MapFrame& mapf, int stamp, bool tileTableLocked );
+        // returns TRUE if the tile was modified as a result of a completed request.
+        bool serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked );
 
-    // returns TRUE if the tile was modified as a result of a completed request.
-    bool serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked );
+        /** Setting this hint tells the tile whether it should bother trying to load elevation data. */
+        void setHasElevationHint( bool hasElevation );
 
-    /** Setting this hint tells the tile whether it should bother trying to load elevation data. */
-    void setHasElevationHint( bool hasElevation );
+        /** Gets whether the tile's real (not placeholder) elevation data has been loaded. */
+        bool isElevationLayerUpToDate() const { return _elevationLayerUpToDate; }
 
-    /** Gets whether the tile's real (not placeholder) elevation data has been loaded. */
-    bool isElevationLayerUpToDate() const { return _elevationLayerUpToDate; }
+        /** Gets or sets the LOD of this tile's current heightfield data. */
+        int getElevationLOD() const { return _elevationLOD; }
+        void setElevationLOD( int lod );
 
-    /** Gets or sets the LOD of this tile's current heightfield data. */
-    int getElevationLOD() const { return _elevationLOD; }
-    void setElevationLOD( int lod );
+        /** Gets the terrain object to which this tile belongs. */
+        class StreamingTerrainNode* getStreamingTerrain();
+        const class StreamingTerrainNode* getStreamingTerrain() const;
 
-    /** Gets the terrain object to which this tile belongs. */
-    class StreamingTerrainNode* getStreamingTerrain();
-    const class StreamingTerrainNode* getStreamingTerrain() const;
+        // updates one image layer
+        void updateImagery( ImageLayer* layer, const MapFrame& mapf, OSGTileFactory* factory );
 
-    // updates one image layer
-    void updateImagery( ImageLayer* layer, const MapFrame& mapf, OSGTileFactory* factory );
+        // are we using a task service for tile generation?
+        bool getUseTileGenRequest() const { return _useTileGenRequest; }
 
-    // are we using a task service for tile generation?
-    bool getUseTileGenRequest() const { return _useTileGenRequest; }
+        // gets this tile's relatives
+        Relative* getFamily() { return _family; }
 
-    // gets this tile's relatives
-    Relative* getFamily() { return _family; }
-
-    // marks a request to regenerate the tile based on the specified change(s).
-    void queueTileUpdate( TileUpdate::Action action, int index =-1 );
-    
-    void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 );
+        // marks a request to regenerate the tile based on the specified change(s).
+        void queueTileUpdate( TileUpdate::Action action, int index =-1 );
+        
+        void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 );
 
-    // marks any pending or running requests for cancelation, and returns true if they
-    // are all canceled.
-    virtual bool cancelActiveTasks(); //override
+        // marks any pending or running requests for cancelation, and returns true if they
+        // are all canceled.
+        virtual bool cancelActiveTasks(); //override
 
-    void resetElevationRequests( const MapFrame& mapf );
+        void resetElevationRequests( const MapFrame& mapf );
 
-protected:
+    protected:
 
-    virtual ~StreamingTile();
+        virtual ~StreamingTile();
 
-private:
+    private:
 
-    bool _requestsInstalled;
-    bool _hasElevation;
-    bool _elevationLayerDirty;
-    bool _colorLayersDirty;
-    bool _elevationLayerRequested;
-    bool _elevationLayerUpToDate;
-    int  _elevationLOD;
-    bool _useTileGenRequest;
-    bool _sequentialImagery;
+        bool _requestsInstalled;
+        bool _hasElevation;
+        bool _elevationLayerDirty;
+        bool _colorLayersDirty;
+        bool _elevationLayerRequested;
+        bool _elevationLayerUpToDate;
+        int  _elevationLOD;
+        bool _useTileGenRequest;
+        bool _sequentialImagery;
 
-    typedef std::queue<TileUpdate> TileUpdateQueue;
-    TileUpdateQueue _tileUpdates;
+        typedef std::queue<TileUpdate> TileUpdateQueue;
+        TileUpdateQueue _tileUpdates;
 
-    TaskRequestList _requests;
-    osg::ref_ptr<TaskRequest> _elevRequest;
-    osg::ref_ptr<TaskRequest> _elevPlaceholderRequest;
-    osg::ref_ptr<TaskRequest> _tileGenRequest;
+        TaskRequestList _requests;
+        osg::ref_ptr<TaskRequest> _elevRequest;
+        osg::ref_ptr<TaskRequest> _elevPlaceholderRequest;
+        osg::ref_ptr<TaskRequest> _tileGenRequest;
 
-    Relative _family[5];
+        Relative _family[5];
 
-    /** Deals with completed requests during the UPDATE traversal. */
-    void installRequests( const MapFrame& mapf, int stamp );
-    bool readyForNewElevation();
-    bool readyForNewImagery(osgEarth::ImageLayer* layer, int currentLOD);
-};
+        /** Deals with completed requests during the UPDATE traversal. */
+        void installRequests( const MapFrame& mapf, int stamp );
+        bool readyForNewElevation();
+        bool readyForNewImagery(osgEarth::ImageLayer* layer, int currentLOD);
+    };
 
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TILE
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp b/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp
index b498164..5760575 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp
@@ -23,7 +23,7 @@
 
 #include <osgEarth/Registry>
 #include <osgEarth/Locators>
-#include <osgEarth/Map>
+#include <osgEarth/MapFrame>
 #include <osgEarth/NodeUtils>
 
 #include <osg/NodeCallback>
@@ -34,6 +34,7 @@
 
 #include <OpenThreads/ScopedLock>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
@@ -89,8 +90,8 @@ namespace
         MapFrame _mapf;
         //osg::ref_ptr<Map> _map;
         osg::ref_ptr<OSGTileFactory> _tileFactory;
-	    unsigned int _numTries;
-	    unsigned int _maxTries;
+        unsigned int _numTries;
+        unsigned int _maxTries;
     };
 
     struct TileColorLayerRequest : public TileLayerRequest
@@ -104,10 +105,10 @@ namespace
             if ( imageLayer.valid() )
             {
                 _result = _tileFactory->createImageLayer( _mapf.getMapInfo(), imageLayer.get(), _key, progress );
-			    if (!wasCanceled())
-			    {
-			      _numTries++;
-			    }
+                if (!wasCanceled())
+                {
+                  _numTries++;
+                }
             }
         }
         UID _layerUID;
@@ -124,7 +125,7 @@ namespace
         void operator()( ProgressCallback* progress )
         {
             _result = _tileFactory->createHeightFieldLayer( _mapf, _key, true ); //exactOnly=true
-		    _numTries++;
+            _numTries++;
         }
     };
 
@@ -410,8 +411,8 @@ StreamingTile::resetElevationRequests( const MapFrame& mapf )
     _elevRequest->setPriority( priority );
     std::stringstream ss;
     ss << "TileElevationLayerRequest " << _key.str() << std::endl;
-	std::string ssStr;
-	ssStr = ss.str();
+    std::string ssStr;
+    ssStr = ss.str();
     _elevRequest->setName( ssStr );
 
     // this request will load placeholder elevation data for the tile:
@@ -421,7 +422,7 @@ StreamingTile::resetElevationRequests( const MapFrame& mapf )
     _elevPlaceholderRequest->setPriority( priority );
     ss.str("");
     ss << "TileElevationPlaceholderLayerRequest " << _key.str() << std::endl;
-	ssStr = ss.str();
+    ssStr = ss.str();
     _elevPlaceholderRequest->setName( ssStr );
 }
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/TerrainNode b/src/osgEarthDrivers/engine_osgterrain/TerrainNode
index b0fa3fb..3e80003 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TerrainNode
+++ b/src/osgEarthDrivers/engine_osgterrain/TerrainNode
@@ -32,121 +32,125 @@
 #include <queue>
 #include <vector>
 
-class TileFactory;
+namespace osgEarth_engine_osgterrain
+{
+    class TileFactory;
 
-using namespace osgEarth;
+    using namespace osgEarth;
 
-/**
- */
-class TerrainNode : public osg::Group //osgTerrain::Terrain
-{
-public:
-    TerrainNode(
-        const MapFrame& update_mapf,
-        const MapFrame& cull_mapf,
-        OSGTileFactory* factory,
-        bool            quickReleaseGLObjects );
+    /**
+     */
+    class TerrainNode : public osg::Group //osgTerrain::Terrain
+    {
+    public:
+        TerrainNode(
+            const MapFrame& update_mapf,
+            const MapFrame& cull_mapf,
+            OSGTileFactory* factory,
+            bool            quickReleaseGLObjects );
 
-    virtual const char* libraryName() const { return "osgEarth"; }
+        virtual const char* libraryName() const { return "osgEarth"; }
 
-    virtual const char* className() const { return "TerrainNode"; }
+        virtual const char* className() const { return "TerrainNode"; }
 
-public:
+    public:
 
-    OSGTileFactory* getTileFactory() { return _tileFactory.get(); }
+        OSGTileFactory* getTileFactory() { return _tileFactory.get(); }
 
-    bool getQuickReleaseGLObjects() const { return _quickReleaseGLObjects; }
+        bool getQuickReleaseGLObjects() const { return _quickReleaseGLObjects; }
 
-    const MapFrame& getUpdateThreadMapFrame() { return _update_mapf; }
+        const MapFrame& getUpdateThreadMapFrame() { return _update_mapf; }
 
-    const MapFrame& getCullThreadMapFrame()   { return _cull_mapf;   }
+        const MapFrame& getCullThreadMapFrame()   { return _cull_mapf;   }
 
-    virtual Tile* createTile( const TileKey& key, GeoLocator* locator ) const;
-    
-    void setTechniquePrototype( TerrainTechnique* tech );
+        virtual Tile* createTile( const TileKey& key, GeoLocator* locator ) const;
+        
+        void setTechniquePrototype( TerrainTechnique* tech );
 
-    TerrainTechnique* cloneTechnique() const;
+        TerrainTechnique* cloneTechnique() const;
 
-    void setSampleRatio( float value );
+        void setSampleRatio( float value );
 
-    float getSampleRatio() const { return _sampleRatio; }
+        float getSampleRatio() const { return _sampleRatio; }
 
-    void setVerticalScale( float value );
+        void setVerticalScale( float value );
 
-    float getVerticalScale() const { return _verticalScale; }
+        float getVerticalScale() const { return _verticalScale; }
 
-    virtual void traverse( osg::NodeVisitor &nv );
+        virtual void traverse( osg::NodeVisitor &nv );
 
-protected:
+    protected:
 
-	virtual ~TerrainNode();
+	    virtual ~TerrainNode();
 
-    // subclass can override to notify of running tasks
-    virtual unsigned getNumActiveTasks() const { return 0; }
+        // subclass can override to notify of running tasks
+        virtual unsigned getNumActiveTasks() const { return 0; }
 
-    // subclass can override this to perform addition UPDATE traversal operations
-    virtual void updateTraversal( osg::NodeVisitor& nv ) { }
+        // subclass can override this to perform addition UPDATE traversal operations
+        virtual void updateTraversal( osg::NodeVisitor& nv ) { }
 
-    typedef std::map< osgTerrain::TileID, osg::ref_ptr<Tile> > TileTable;
+        typedef std::map< osgTerrain::TileID, osg::ref_ptr<Tile> > TileTable;
 
-    typedef std::queue< osg::ref_ptr<Tile> >  TileQueue;
-    typedef std::list< osg::ref_ptr<Tile> >   TileList;
-    typedef std::vector< osg::ref_ptr<Tile> > TileVector;
-    typedef std::queue< osgTerrain::TileID >  TileIDQueue;
+        typedef std::queue< osg::ref_ptr<Tile> >  TileQueue;
+        typedef std::list< osg::ref_ptr<Tile> >   TileList;
+        typedef std::vector< osg::ref_ptr<Tile> > TileVector;
+        typedef std::queue< osgTerrain::TileID >  TileIDQueue;
 
-    Threading::ReadWriteMutex _tilesMutex;
-    TileTable                 _tiles;
-    TileList                  _tilesToShutDown;
-    TileVector                _tilesToRelease;
-    Threading::Mutex          _tilesToReleaseMutex;
+        Threading::ReadWriteMutex _tilesMutex;
+        TileTable                 _tiles;
+        TileList                  _tilesToShutDown;
+        TileVector                _tilesToRelease;
+        Threading::Mutex          _tilesToReleaseMutex;
 
-    float _sampleRatio;
-    float _verticalScale;
+        float _sampleRatio;
+        float _verticalScale;
 
-public:
+    public:
 
-    void releaseGLObjectsForTiles( osg::State* state );
+        void releaseGLObjectsForTiles( osg::State* state );
 
-    void registerTile( Tile* newTile );
+        void registerTile( Tile* newTile );
 
-    /** Gets a thread-safe copy of the entire tile list */
-    void getTiles( TileVector& out_tiles );
+        /** Gets a thread-safe copy of the entire tile list */
+        void getTiles( TileVector& out_tiles );
 
-    /** Fetches a tile from the repo */
-    template<typename T>
-    void getTile(const osgTerrain::TileID& id, osg::ref_ptr<T>& out_tile, bool lock =true ) const {
-        if ( lock ) {
-            Threading::ScopedReadLock lock( const_cast<TerrainNode*>(this)->_tilesMutex );
-            TileTable::const_iterator i = _tiles.find( id );
-            out_tile = i != _tiles.end()? static_cast<T*>(i->second.get()) : 0L;
+        /** Fetches a tile from the repo */
+        template<typename T>
+        void getTile(const osgTerrain::TileID& id, osg::ref_ptr<T>& out_tile, bool lock =true ) const {
+            if ( lock ) {
+                Threading::ScopedReadLock lock( const_cast<TerrainNode*>(this)->_tilesMutex );
+                TileTable::const_iterator i = _tiles.find( id );
+                out_tile = i != _tiles.end()? static_cast<T*>(i->second.get()) : 0L;
+            }
+            else {
+                TileTable::const_iterator i = _tiles.find( id );
+                out_tile = i != _tiles.end()? static_cast<T*>(i->second.get()) : 0L;
+            }
         }
-        else {
-            TileTable::const_iterator i = _tiles.find( id );
-            out_tile = i != _tiles.end()? static_cast<T*>(i->second.get()) : 0L;
-        }
-    }
 
-protected:
+    protected:
+
+        osg::ref_ptr<OSGTileFactory> _tileFactory;
+        osg::ref_ptr<const Profile>  _profile;
 
-    osg::ref_ptr<OSGTileFactory> _tileFactory;
-    osg::ref_ptr<const Profile>  _profile;
+        bool _alwaysUpdate;
+        int  _onDemandDelay; // #frames
 
-    bool _alwaysUpdate;
-    int  _onDemandDelay; // #frames
+        void setDelay( unsigned frames );
+        void decDelay();
 
-    void setDelay( unsigned frames );
-    void decDelay();
+	    bool _registeredWithReleaseGLCallback;
 
-	bool _registeredWithReleaseGLCallback;
+        // store a separate map frame for each of the traversal threads
+        const MapFrame& _update_mapf; // map frame for the main/update traversal thread
+        const MapFrame& _cull_mapf;   // map frame for the cull traversal thread
 
-    // store a separate map frame for each of the traversal threads
-    const MapFrame& _update_mapf; // map frame for the main/update traversal thread
-    const MapFrame& _cull_mapf;   // map frame for the cull traversal thread
+        bool _quickReleaseGLObjects;
+        bool _quickReleaseCallbackInstalled;
 
-    bool _quickReleaseGLObjects;
-    bool _quickReleaseCallbackInstalled;
+        osg::ref_ptr<TerrainTechnique> _techPrototype;
+    };
 
-    osg::ref_ptr<TerrainTechnique> _techPrototype;
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_STANDARD_TERRAIN
diff --git a/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp b/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp
index 3bf02f9..975f1fc 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp
@@ -30,6 +30,7 @@
 #include <osg/Node>
 #include <osgGA/EventVisitor>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/Tile b/src/osgEarthDrivers/engine_osgterrain/Tile
index 4276ef3..b2651d2 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Tile
+++ b/src/osgEarthDrivers/engine_osgterrain/Tile
@@ -34,170 +34,174 @@
 #include <queue>
 #include <iterator>
 
-class TileFactory;
-class TerrainTechnique;
-
-using namespace osgEarth;
-
-typedef std::map<UID, CustomColorLayer> ColorLayersByUID;
+namespace osgEarth_engine_osgterrain
+{
+    class TileFactory;
+    class TerrainTechnique;
 
-//------------------------------------------------------------------------
+    using namespace osgEarth;
 
-// Records a tile change request so that we can update tiles piecemeal
-class TileUpdate
-{
-public:
-    enum Action {
-        ADD_IMAGE_LAYER,
-        REMOVE_IMAGE_LAYER,
-        MOVE_IMAGE_LAYER,
-        UPDATE_IMAGE_LAYER,
-        UPDATE_ALL_IMAGE_LAYERS,
-        UPDATE_ELEVATION,
-        UPDATE_ALL
-    };
+    typedef std::map<UID, CustomColorLayer> ColorLayersByUID;
 
-    TileUpdate( Action action, UID layerUID =(UID)-1 ) 
-        : _action(action), _layerUID(layerUID) { }
+    //------------------------------------------------------------------------
 
-    Action getAction() const { return _action; }
+    // Records a tile change request so that we can update tiles piecemeal
+    class TileUpdate
+    {
+    public:
+        enum Action {
+            ADD_IMAGE_LAYER,
+            REMOVE_IMAGE_LAYER,
+            MOVE_IMAGE_LAYER,
+            UPDATE_IMAGE_LAYER,
+            UPDATE_ALL_IMAGE_LAYERS,
+            UPDATE_ELEVATION,
+            UPDATE_ALL
+        };
 
-    UID getLayerUID() const { return _layerUID; }
+        TileUpdate( Action action, UID layerUID =(UID)-1 ) 
+            : _action(action), _layerUID(layerUID) { }
 
-private:
-    Action _action;
-    UID _layerUID;
-};
+        Action getAction() const { return _action; }
 
-//------------------------------------------------------------------------
+        UID getLayerUID() const { return _layerUID; }
 
-class Tile : public osg::Node
-{
-public:
-    Tile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects );
+    private:
+        Action _action;
+        UID _layerUID;
+    };
 
-    /** Gets the tilekey associated with this tile. */
-    const TileKey& getKey() const { return _key; }
+    //------------------------------------------------------------------------
 
-    /** Gets the terrain object to which this tile belongs. */
-    class TerrainNode* getTerrain() { return _terrain.get(); }
-    const class TerrainNode* getTerrain() const { return _terrain.get(); }
+    class Tile : public osg::Node
+    {
+    public:
+        Tile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects );
 
-    // attaches this tile to a terrain and registers it.
-    void attachToTerrain( TerrainNode* terrain );
+        /** Gets the tilekey associated with this tile. */
+        const TileKey& getKey() const { return _key; }
 
-    /** intializes the tile and clears its dirty flag */
-    void init();
+        /** Gets the terrain object to which this tile belongs. */
+        class TerrainNode* getTerrain() { return _terrain.get(); }
+        const class TerrainNode* getTerrain() const { return _terrain.get(); }
 
-    /** Gets or sets the terrain mask geometry. */
-    const MaskLayerVector& getTerrainMasks() { return _masks; }
-    void setTerrainMasks(const MaskLayerVector& terrainMask) { _masks.clear(); std::copy( terrainMask.begin(), terrainMask.end(), std::back_inserter(_masks) ); }
+        // attaches this tile to a terrain and registers it.
+        void attachToTerrain( TerrainNode* terrain );
 
-    /** Whether OSG has traversed the tile at least once. */
-    bool getHasBeenTraversed() const { return _hasBeenTraversed; }
+        /** intializes the tile and clears its dirty flag */
+        void init();
 
-    /** Mutex that protects access to the tile contents. */
-    Threading::ReadWriteMutex& getTileLayersMutex() { return _tileLayersMutex; }
+        /** Gets or sets the terrain mask geometry. */
+        const MaskLayerVector& getTerrainMasks() { return _masks; }
+        void setTerrainMasks(const MaskLayerVector& terrainMask) { _masks.clear(); std::copy( terrainMask.begin(), terrainMask.end(), std::back_inserter(_masks) ); }
 
-    // marks a request to regenerate the tile based on the specified change(s).
-    virtual void queueTileUpdate( TileUpdate::Action action, int index =-1 );
-    
-    void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 );
+        /** Whether OSG has traversed the tile at least once. */
+        bool getHasBeenTraversed() const { return _hasBeenTraversed; }
 
-    virtual bool cancelActiveTasks() { return true; }
+        /** Mutex that protects access to the tile contents. */
+        Threading::ReadWriteMutex& getTileLayersMutex() { return _tileLayersMutex; }
 
-    /** The scale factor for elevation heights */
-    float getVerticalScale() const { return _verticalScale; }
-    void setVerticalScale( float verticalScale );
+        // marks a request to regenerate the tile based on the specified change(s).
+        virtual void queueTileUpdate( TileUpdate::Action action, int index =-1 );
+        
+        void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 );
 
-    osgTerrain::Locator* getLocator() const { return _locator.get(); }
+        virtual bool cancelActiveTasks() { return true; }
 
-    const osgTerrain::TileID& getTileId() const { return _tileId; }
+        /** The scale factor for elevation heights */
+        float getVerticalScale() const { return _verticalScale; }
+        void setVerticalScale( float verticalScale );
 
-    bool getDirty() const { return _dirty; }
-    void setDirty( bool value ) { _dirty = value; }
+        osgTerrain::Locator* getLocator() const { return _locator.get(); }
 
-    void setTerrainTechnique( TerrainTechnique* value );
-    TerrainTechnique* getTerrainTechnique() const { return _tech.get(); }
+        const osgTerrain::TileID& getTileId() const { return _tileId; }
 
-    /** Tile contents. We don't use the TerrainTile color layer list, we use our own */
-    void removeCustomColorLayer( UID layerUID, bool writeLock =true );
-    bool getCustomColorLayer( UID layerUID, CustomColorLayer& output, bool readLock =true ) const;
-    void getCustomColorLayers( ColorLayersByUID& out, bool readLock =true ) const;
-    void setCustomColorLayers( const ColorLayersByUID& in, bool writeLock =true );
-    void setCustomColorLayer( const CustomColorLayer& colorLayer, bool writeLock =true );
+        bool getDirty() const { return _dirty; }
+        void setDirty( bool value ) { _dirty = value; }
 
-    void clear();
+        void setTerrainTechnique( TerrainTechnique* value );
+        TerrainTechnique* getTerrainTechnique() const { return _tech.get(); }
 
-    osgTerrain::HeightFieldLayer* getElevationLayer() const { return _elevationLayer.get(); }
-    void setElevationLayer( osgTerrain::HeightFieldLayer* value ) { _elevationLayer = value; }
+        /** Tile contents. We don't use the TerrainTile color layer list, we use our own */
+        void removeCustomColorLayer( UID layerUID, bool writeLock =true );
+        bool getCustomColorLayer( UID layerUID, CustomColorLayer& output, bool readLock =true ) const;
+        void getCustomColorLayers( ColorLayersByUID& out, bool readLock =true ) const;
+        void setCustomColorLayers( const ColorLayersByUID& in, bool writeLock =true );
+        void setCustomColorLayer( const CustomColorLayer& colorLayer, bool writeLock =true );
 
-public: // OVERRIDES
+        void clear();
 
-    virtual void traverse( class osg::NodeVisitor& nv );
+        osgTerrain::HeightFieldLayer* getElevationLayer() const { return _elevationLayer.get(); }
+        void setElevationLayer( osgTerrain::HeightFieldLayer* value ) { _elevationLayer = value; }
 
-    /** If State is non-zero, this function releases any associated OpenGL objects for
-      * the specified graphics context. Otherwise, releases OpenGL objects
-      * for all graphics contexts. */
-    virtual void releaseGLObjects(osg::State* = 0) const;
+    public: // OVERRIDES
 
-    virtual osg::BoundingSphere computeBound() const;
+        virtual void traverse( class osg::NodeVisitor& nv );
 
-protected:
+        /** If State is non-zero, this function releases any associated OpenGL objects for
+          * the specified graphics context. Otherwise, releases OpenGL objects
+          * for all graphics contexts. */
+        virtual void releaseGLObjects(osg::State* = 0) const;
 
-    virtual ~Tile();
+        virtual osg::BoundingSphere computeBound() const;
 
-    bool _hasBeenTraversed;
-    bool _quickReleaseGLObjects;
-    bool _parentTileSet;
-    bool _dirty;
+    protected:
 
-    TileKey                        _key;
-    osgTerrain::TileID             _tileId;
-    osg::ref_ptr<GeoLocator>       _locator;
-    osg::observer_ptr<TerrainNode> _terrain;
-    MaskLayerVector                _masks;
+        virtual ~Tile();
 
-    Threading::ReadWriteMutex _tileLayersMutex;
-    ColorLayersByUID          _colorLayers;
-    float                     _verticalScale;
+        bool _hasBeenTraversed;
+        bool _quickReleaseGLObjects;
+        bool _parentTileSet;
+        bool _dirty;
 
-    osg::ref_ptr<osgTerrain::HeightFieldLayer> _elevationLayer;
-    osg::ref_ptr<TerrainTechnique>             _tech;
+        TileKey                        _key;
+        osgTerrain::TileID             _tileId;
+        osg::ref_ptr<GeoLocator>       _locator;
+        osg::observer_ptr<TerrainNode> _terrain;
+        MaskLayerVector                _masks;
 
+        Threading::ReadWriteMutex _tileLayersMutex;
+        ColorLayersByUID          _colorLayers;
+        float                     _verticalScale;
 
-public:
-    friend class TileFrame;
-};
+        osg::ref_ptr<osgTerrain::HeightFieldLayer> _elevationLayer;
+        osg::ref_ptr<TerrainTechnique>             _tech;
 
-class TileVector : public std::vector< osg::ref_ptr<Tile> > { };
 
-// --------------------------------------------------------------------------
+    public:
+        friend class TileFrame;
+    };
 
-/**
- * Thread-safe working copy of Tile contents.
- */
-class TileFrame
-{
-public:
-    TileFrame( Tile* tile );
-
-    TileKey                                      _tileKey;
-    ColorLayersByUID                             _colorLayers;
-    osg::ref_ptr< osgTerrain::HeightFieldLayer > _elevationLayer;
-    osg::ref_ptr< osgTerrain::Locator >          _locator;
-    float                                        _sampleRatio;
-    MaskLayerVector                              _masks;
-
-    // convenience funciton to pull out a layer by its UID.
-    bool getCustomColorLayer( UID layerUID, CustomColorLayer& out ) const {
-        ColorLayersByUID::const_iterator i = _colorLayers.find( layerUID );
-        if ( i != _colorLayers.end() ) {
-            out = i->second;
-            return true;
+    class TileVector : public std::vector< osg::ref_ptr<Tile> > { };
+
+    // --------------------------------------------------------------------------
+
+    /**
+     * Thread-safe working copy of Tile contents.
+     */
+    class TileFrame
+    {
+    public:
+        TileFrame( Tile* tile );
+
+        TileKey                                      _tileKey;
+        ColorLayersByUID                             _colorLayers;
+        osg::ref_ptr< osgTerrain::HeightFieldLayer > _elevationLayer;
+        osg::ref_ptr< osgTerrain::Locator >          _locator;
+        float                                        _sampleRatio;
+        MaskLayerVector                              _masks;
+
+        // convenience funciton to pull out a layer by its UID.
+        bool getCustomColorLayer( UID layerUID, CustomColorLayer& out ) const {
+            ColorLayersByUID::const_iterator i = _colorLayers.find( layerUID );
+            if ( i != _colorLayers.end() ) {
+                out = i->second;
+                return true;
+            }
+            return false;
         }
-        return false;
-    }
-};
+    };
+
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TILE
diff --git a/src/osgEarthDrivers/engine_osgterrain/Tile.cpp b/src/osgEarthDrivers/engine_osgterrain/Tile.cpp
index ce3462f..208a1dd 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Tile.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/Tile.cpp
@@ -34,7 +34,7 @@
 
 #include <OpenThreads/ScopedLock>
 
-
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/TileBuilder b/src/osgEarthDrivers/engine_osgterrain/TileBuilder
index 8924f28..f7b6c49 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TileBuilder
+++ b/src/osgEarthDrivers/engine_osgterrain/TileBuilder
@@ -21,78 +21,83 @@
 
 #include "Common"
 #include "Tile"
-#include <osgEarth/Map>
+#include <osgEarth/MapInfo>
+#include <osgEarth/MapFrame>
 #include <osgEarth/TaskService>
 #include <osg/Group>
 
-using namespace osgEarth;
-
-class TileBuilder : public osg::Referenced
+namespace osgEarth_engine_osgterrain
 {
-public:
-    struct SourceRepo
-    {
-        SourceRepo() { }
+    using namespace osgEarth;
 
-        void add( const CustomColorLayer& layer )
+    class TileBuilder : public osg::Referenced
+    {
+    public:
+        struct SourceRepo
         {
-            Threading::ScopedMutexLock lock(_m);
-            _colorLayers[ layer.getUID() ] = layer;
-        }
-
-        void set( const CustomElevLayer& elevLayer )
+            SourceRepo() { }
+
+            void add( const CustomColorLayer& layer )
+            {
+                Threading::ScopedMutexLock lock(_m);
+                _colorLayers[ layer.getUID() ] = layer;
+            }
+
+            void set( const CustomElevLayer& elevLayer )
+            {
+                // only one...no lock required
+                _elevLayer = elevLayer;
+            }
+
+            ColorLayersByUID _colorLayers;
+            CustomElevLayer _elevLayer;
+            Threading::Mutex _m;
+        };
+
+        struct Job : public osg::Referenced
         {
-            // only one...no lock required
-            _elevLayer = elevLayer;
-        }
-
-        ColorLayersByUID _colorLayers;
-        CustomElevLayer _elevLayer;
-        Threading::Mutex _m;
-    };
-
-    struct Job : public osg::Referenced
-    {
-        Job(const TileKey& key, const Map* map) : _key(key), _mapf(map, Map::TERRAIN_LAYERS) { }
-
-        TileKey           _key;
-        MapFrame          _mapf;
-        SourceRepo        _repo;
-        TaskRequestVector _tasks;
+            Job(const TileKey& key, const Map* map) : _key(key), _mapf(map, Map::TERRAIN_LAYERS) { }
+
+            TileKey           _key;
+            MapFrame          _mapf;
+            SourceRepo        _repo;
+            TaskRequestVector _tasks;
+        };
+
+    public:
+        TileBuilder(
+            const Map*               map,
+            const OSGTerrainOptions& terrainOptions,
+            TaskService*             service );
+
+        /** dtor */
+        virtual ~TileBuilder() { }
+
+        void createTile(
+            const TileKey&      key,
+            bool                parallelize,
+            osg::ref_ptr<Tile>& out_tile,
+            bool&               out_hasRealData,
+            bool&               out_hasLodBlendedLayers );
+
+        Job* createJob( const TileKey& key, Threading::MultiEvent& semaphore );
+
+        void runJob( Job* job );
+
+        void finalizeJob( 
+            Job*                job, 
+            osg::ref_ptr<Tile>& out_tile,
+            bool&               out_hasRealData,
+            bool&               out_hasLodBlending );
+
+        TaskService* getTaskService() const { return _service; }
+
+    private:
+        const Map*               _map;
+        TaskService*             _service;
+        const OSGTerrainOptions& _terrainOptions;
     };
 
-public:
-    TileBuilder(
-        const Map*               map,
-        const OSGTerrainOptions& terrainOptions,
-        TaskService*             service );
-
-    /** dtor */
-    virtual ~TileBuilder() { }
-
-    void createTile(
-        const TileKey&      key,
-        bool                parallelize,
-        osg::ref_ptr<Tile>& out_tile,
-        bool&               out_hasRealData,
-        bool&               out_hasLodBlendedLayers );
-
-    Job* createJob( const TileKey& key, Threading::MultiEvent& semaphore );
-
-    void runJob( Job* job );
-
-    void finalizeJob( 
-        Job*                job, 
-        osg::ref_ptr<Tile>& out_tile,
-        bool&               out_hasRealData,
-        bool&               out_hasLodBlending );
-
-    TaskService* getTaskService() const { return _service; }
-
-private:
-    const Map*               _map;
-    TaskService*             _service;
-    const OSGTerrainOptions& _terrainOptions;
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_TILE_BUILDER
diff --git a/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp b/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp
index e2eb1b6..ca89060 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp
@@ -22,6 +22,7 @@
 #include <osgEarth/TaskService>
 #include <osgEarth/HeightFieldUtils>
 
+using namespace osgEarth_engine_osgterrain;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/TransparentLayer b/src/osgEarthDrivers/engine_osgterrain/TransparentLayer
index 9eddd35..be6fe54 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TransparentLayer
+++ b/src/osgEarthDrivers/engine_osgterrain/TransparentLayer
@@ -28,103 +28,107 @@
 #include <osgEarth/TileKey>
 #include <osg/Image>
 
-class CustomColorLayer
+namespace osgEarth_engine_osgterrain
 {
-public:
-    CustomColorLayer() { }
-
-    /** dtor */
-    virtual ~CustomColorLayer() { }
-
-    CustomColorLayer(
-        const osgEarth::ImageLayer* imageLayer,
-        osg::Image* image,
-        const osgTerrain::Locator* locator,
-        int lod,
-        const osgEarth::TileKey& tileKey,
-        bool fallbackData =false )
-        : _layer(imageLayer), _locator(locator), _image(image),  _tileKey(tileKey), _lod(lod), _fallbackData(fallbackData) { }
-
-    CustomColorLayer( const CustomColorLayer& rhs ) :
-        _layer( rhs._layer.get() ),        
-        _locator( rhs._locator.get() ),
-        _image( rhs._image.get() ),
-        _tileKey( rhs._tileKey ),
-        _lod( rhs._lod ),
-        _fallbackData( rhs._fallbackData ) { }
-
-    osgEarth::UID getUID() const {
-        return _layer->getUID();
-    }
-
-    const osgTerrain::Locator* getLocator() const {
-        return _locator.get();
-    }
-
-    osg::Image* getImage() const { 
-        return _image.get(); }
-
-    const osgEarth::TileKey& getTileKey() const {
-        return _tileKey; }
-
-    const osgEarth::ImageLayer* getMapLayer() const {
-        return _layer.get(); }
-
-    int getLevelOfDetail() const {
-        return _lod; }
-
-    bool isFallbackData() const {
-        return _fallbackData; }
-
-    osg::BoundingSphere computeBound() const {
-        osg::BoundingSphere bs;
-        osg::Vec3d v;
-        if (getLocator()->convertLocalToModel(osg::Vec3d(0.5,0.5,0.0), v)) {
-            bs.center() = v;
+    class CustomColorLayer
+    {
+    public:
+        CustomColorLayer() { }
+
+        /** dtor */
+        virtual ~CustomColorLayer() { }
+
+        CustomColorLayer(
+            const osgEarth::ImageLayer* imageLayer,
+            osg::Image* image,
+            const osgTerrain::Locator* locator,
+            int lod,
+            const osgEarth::TileKey& tileKey,
+            bool fallbackData =false )
+            : _layer(imageLayer), _locator(locator), _image(image),  _tileKey(tileKey), _lod(lod), _fallbackData(fallbackData) { }
+
+        CustomColorLayer( const CustomColorLayer& rhs ) :
+            _layer( rhs._layer.get() ),        
+            _locator( rhs._locator.get() ),
+            _image( rhs._image.get() ),
+            _tileKey( rhs._tileKey ),
+            _lod( rhs._lod ),
+            _fallbackData( rhs._fallbackData ) { }
+
+        osgEarth::UID getUID() const {
+            return _layer->getUID();
         }
-        if (getLocator()->convertLocalToModel(osg::Vec3d(0.0,0.0,0.0), v)) {
-            bs.radius() = (bs.center() - v).length();
+
+        const osgTerrain::Locator* getLocator() const {
+            return _locator.get();
         }
-        return bs;
-    }
 
+        osg::Image* getImage() const { 
+            return _image.get(); }
 
-private:
-    osg::ref_ptr<const osgEarth::ImageLayer> _layer;
-    osg::ref_ptr<const osgTerrain::Locator>  _locator;
-    osg::ref_ptr<osg::Image>                 _image;
-    osgEarth::TileKey                        _tileKey;
-    int                                      _lod;
-    bool                                     _fallbackData;
-};
+        const osgEarth::TileKey& getTileKey() const {
+            return _tileKey; }
 
-class CustomColorLayerRef : public osg::Referenced
-{
-public:
-    CustomColorLayerRef( const CustomColorLayer& layer ) : _layer(layer) { }
-    CustomColorLayer _layer;
-};
+        const osgEarth::ImageLayer* getMapLayer() const {
+            return _layer.get(); }
 
-class CustomElevLayer
-{
-public:
-    CustomElevLayer() { }
+        int getLevelOfDetail() const {
+            return _lod; }
+
+        bool isFallbackData() const {
+            return _fallbackData; }
+
+        osg::BoundingSphere computeBound() const {
+            osg::BoundingSphere bs;
+            osg::Vec3d v;
+            if (getLocator()->convertLocalToModel(osg::Vec3d(0.5,0.5,0.0), v)) {
+                bs.center() = v;
+            }
+            if (getLocator()->convertLocalToModel(osg::Vec3d(0.0,0.0,0.0), v)) {
+                bs.radius() = (bs.center() - v).length();
+            }
+            return bs;
+        }
+
+
+    private:
+        osg::ref_ptr<const osgEarth::ImageLayer> _layer;
+        osg::ref_ptr<const osgTerrain::Locator>  _locator;
+        osg::ref_ptr<osg::Image>                 _image;
+        osgEarth::TileKey                        _tileKey;
+        int                                      _lod;
+        bool                                     _fallbackData;
+    };
+
+    class CustomColorLayerRef : public osg::Referenced
+    {
+    public:
+        CustomColorLayerRef( const CustomColorLayer& layer ) : _layer(layer) { }
+        CustomColorLayer _layer;
+    };
+
+    class CustomElevLayer
+    {
+    public:
+        CustomElevLayer() { }
+
+        /** dtor */
+        virtual ~CustomElevLayer() { }
 
-    /** dtor */
-    virtual ~CustomElevLayer() { }
+        CustomElevLayer( osgTerrain::HeightFieldLayer* hfLayer, bool fallbackData =false )
+            : _hfLayer(hfLayer), _fallbackData(fallbackData) { }
 
-    CustomElevLayer( osgTerrain::HeightFieldLayer* hfLayer, bool fallbackData =false )
-        : _hfLayer(hfLayer), _fallbackData(fallbackData) { }
+        osgTerrain::HeightFieldLayer* getHFLayer() {
+            return _hfLayer.get(); }
 
-    osgTerrain::HeightFieldLayer* getHFLayer() {
-        return _hfLayer.get(); }
+        bool isFallbackData() const {
+            return _fallbackData; }
 
-    bool isFallbackData() const {
-        return _fallbackData; }
+    private:
+        osg::ref_ptr<osgTerrain::HeightFieldLayer> _hfLayer;
+        bool _fallbackData;
+    };
 
-private:
-    osg::ref_ptr<osgTerrain::HeightFieldLayer> _hfLayer;
-    bool _fallbackData;
-};
+} // namespace osgEarth_engine_osgterrain
 
 #endif // OSGEARTH_ENGINE_TRANSPARENT_LAYER
diff --git a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD b/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
index f58cd94..477f051 100644
--- a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
+++ b/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
@@ -26,44 +26,48 @@
 
 using namespace osgEarth;
 
-/**
- * Customized PagedLOD node that automatically registers TileNodes
- * with a TileNodeRegistry when they are added to the scene graph.
- */
-class CustomPagedLOD : public osg::PagedLOD
+namespace osgEarth_engine_quadtree
 {
-public:
     /**
-     * Constructs a PagedLOD node that will register TileNode's with the 
-     * provided registry 
+     * Customized PagedLOD node that automatically registers TileNodes
+     * with a TileNodeRegistry when they are added to the scene graph.
      */
-    CustomPagedLOD(
-        TileNodeRegistry* liveTiles,
-        TileNodeRegistry* deadTiles );
+    class CustomPagedLOD : public osg::PagedLOD
+    {
+    public:
+        /**
+         * Constructs a PagedLOD node that will register TileNode's with the 
+         * provided registry 
+         */
+        CustomPagedLOD(
+            TileNodeRegistry* liveTiles,
+            TileNodeRegistry* deadTiles );
 
-    /**
-     * Destructor 
-     */
-    virtual ~CustomPagedLOD();
+        /**
+         * Destructor 
+         */
+        virtual ~CustomPagedLOD();
 
-public: // osg::Group
+    public: // osg::Group
 
-    /**
-     * Override Group methods so that a TileNode gets registered when the 
-     * DatabasePager adds it to the scene graph. Of course this would happen
-     * if you simply added the TileNode to any parent, but in this 
-     * implmementation the DatabasePager is the only entity adding a TileNode
-     * to a parent.
-     *
-     * DatabasePager only calls addChild or removeChild, but we will implement the
-     * other methods (insert, replace, etc) later if necessary
-     */
-    bool addChild( osg::Node* child );
-    bool removeChildren(unsigned pos, unsigned numChildrenToRemove );
+        /**
+         * Override Group methods so that a TileNode gets registered when the 
+         * DatabasePager adds it to the scene graph. Of course this would happen
+         * if you simply added the TileNode to any parent, but in this 
+         * implmementation the DatabasePager is the only entity adding a TileNode
+         * to a parent.
+         *
+         * DatabasePager only calls addChild or removeChild, but we will implement the
+         * other methods (insert, replace, etc) later if necessary
+         */
+        bool addChild( osg::Node* child );
+        bool removeChildren(unsigned pos, unsigned numChildrenToRemove );
+
+    private:
 
-private:
+        osg::ref_ptr<TileNodeRegistry> _live, _dead;
+    };
 
-    osg::ref_ptr<TileNodeRegistry> _live, _dead;
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_CUSTOM_PAGED_LOD
diff --git a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD.cpp b/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD.cpp
index 2e69801..52b1a0f 100644
--- a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD.cpp
@@ -18,6 +18,7 @@
 */
 #include "CustomPagedLOD"
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 
 #define LC "[CustomPagedLOD] "
diff --git a/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback b/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
index 62cbc7c..0f8b31f 100644
--- a/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
+++ b/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
@@ -23,59 +23,63 @@
 #include <osg/NodeCallback>
 #include <osg/CullStack>
 
-/**
- * Cull callback that dynamically computes an LOD scale based on
- * distance to the camera and a "fall off" metric. As the fall off
- * increases, farther objects' LOD scale will increase. A good
- * range for the fall-off number is 0..5.
- */
-struct DynamicLODScaleCallback : public osg::NodeCallback 
+namespace osgEarth_engine_quadtree
 {
-    DynamicLODScaleCallback( float fallOff ) : _fallOff(fallOff) { }
+    /**
+     * Cull callback that dynamically computes an LOD scale based on
+     * distance to the camera and a "fall off" metric. As the fall off
+     * increases, farther objects' LOD scale will increase. A good
+     * range for the fall-off number is 0..5.
+     */
+    struct DynamicLODScaleCallback : public osg::NodeCallback 
+    {
+        DynamicLODScaleCallback( float fallOff ) : _fallOff(fallOff) { }
 
-    /** dtor */
-    virtual ~DynamicLODScaleCallback() { }
+        /** dtor */
+        virtual ~DynamicLODScaleCallback() { }
 
-    void operator()( osg::Node* node, osg::NodeVisitor* nv )
-    {
-        osg::CullStack* cs = dynamic_cast<osg::CullStack*>(nv);
-        if ( cs )
+        void operator()( osg::Node* node, osg::NodeVisitor* nv )
         {
-            osg::LOD* lod = static_cast<osg::LOD*>( node );
-            osg::Vec3 center = lod->getCenter(); 
+            osg::CullStack* cs = dynamic_cast<osg::CullStack*>(nv);
+            if ( cs )
+            {
+                osg::LOD* lod = static_cast<osg::LOD*>( node );
+                osg::Vec3 center = lod->getCenter(); 
 
-            osg::Vec3 eye = nv->getEyePoint();
-            osg::Vec3 eyeVec = eye; eyeVec.normalize();
-            float has = osg::clampAbove( eye.length() - 6356752.3142f, 0.0f );
-            float centerToEye = nv->getDistanceToViewPoint(center, false);
-            float bsToEye = centerToEye - lod->getChild(0)->getBound().radius();
+                osg::Vec3 eye = nv->getEyePoint();
+                osg::Vec3 eyeVec = eye; eyeVec.normalize();
+                float has = osg::clampAbove( eye.length() - 6356752.3142f, 0.0f );
+                float centerToEye = nv->getDistanceToViewPoint(center, false);
+                float bsToEye = centerToEye - lod->getChild(0)->getBound().radius();
 
-            float scaleAdj = 1.0f;
-            if ( bsToEye > has )
+                float scaleAdj = 1.0f;
+                if ( bsToEye > has )
+                {
+                    float denom = osg::maximum(0.1f, (1.0f/_fallOff)) * 10000.0f;
+                    scaleAdj = osg::clampBetween( log10f(bsToEye/denom), 1.0f, 3.0f );
+                    
+                    //OE_INFO << LC 
+                    //    << std::fixed
+                    //    << "centerToEye=" << centerToEye 
+                    //    << ", bsToEye=" << bsToEye
+                    //    << ", scaleAdj=" << scaleAdj
+                    //    << std::endl;
+                }
+
+                float lodScale = cs->getLODScale();
+                cs->setLODScale( lodScale * scaleAdj );
+                traverse( node, nv );
+                cs->setLODScale( lodScale );
+            }
+            else
             {
-                float denom = osg::maximum(0.1f, (1.0f/_fallOff)) * 10000.0f;
-                scaleAdj = osg::clampBetween( log10f(bsToEye/denom), 1.0f, 3.0f );
-                
-                //OE_INFO << LC 
-                //    << std::fixed
-                //    << "centerToEye=" << centerToEye 
-                //    << ", bsToEye=" << bsToEye
-                //    << ", scaleAdj=" << scaleAdj
-                //    << std::endl;
+                traverse( node, nv );
             }
-
-            float lodScale = cs->getLODScale();
-            cs->setLODScale( lodScale * scaleAdj );
-            traverse( node, nv );
-            cs->setLODScale( lodScale );
-        }
-        else
-        {
-            traverse( node, nv );
         }
-    }
 
-    float _fallOff;
-};
+        float _fallOff;
+    };
+
+} // namespace osgEarth_engine_quadtree
 
 #endif //OSGEARTH_ENGINE_OSGTERRAIN_DYNAMIC_LOD_SCALE_CALLBACK_H
diff --git a/src/osgEarthDrivers/engine_quadtree/FileLocationCallback b/src/osgEarthDrivers/engine_quadtree/FileLocationCallback
index 91179a7..ae0533b 100644
--- a/src/osgEarthDrivers/engine_quadtree/FileLocationCallback
+++ b/src/osgEarthDrivers/engine_quadtree/FileLocationCallback
@@ -27,54 +27,59 @@
 
 #define USE_FILELOCATIONCALLBACK OSG_MIN_VERSION_REQUIRED(2,9,5)
 
+
 #if USE_FILELOCATIONCALLBACK
 
-/**
- * A database pager callback that determines if the given filename is cached or not
- */
-class FileLocationCallback : public osgDB::FileLocationCallback
+namespace osgEarth_engine_quadtree
 {
-public:
-    FileLocationCallback() { }
-
-    /** dtor */
-    virtual ~FileLocationCallback() { }
-
-    virtual Location fileLocation(const std::string& filename, const osgDB::Options* options)
+    /**
+     * A database pager callback that determines if the given filename is cached or not
+     */
+    class FileLocationCallback : public osgDB::FileLocationCallback
     {
-        Location result = REMOTE_FILE;
-        //OE_NOTICE<<"fileLocation = "<<filename<<std::endl;
-
-        unsigned int lod, x, y, id;
-        sscanf(filename.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &id);
+    public:
+        FileLocationCallback() { }
 
-        osg::ref_ptr<QuadTreeTerrainEngineNode> engine;
-        QuadTreeTerrainEngineNode::getEngineByUID( (UID)id, engine );
+        /** dtor */
+        virtual ~FileLocationCallback() { }
 
-        if ( engine.valid() )
+        virtual Location fileLocation(const std::string& filename, const osgDB::Options* options)
         {
-            const osgEarth::Profile* profile = engine->getMap()->getProfile();
-            osgEarth::TileKey mapKey( lod, x, y, profile );
+            Location result = REMOTE_FILE;
+            //OE_NOTICE<<"fileLocation = "<<filename<<std::endl;
 
-            result = LOCAL_FILE;
+            unsigned int lod, x, y, id;
+            sscanf(filename.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &id);
 
-            MapFrame mapf( engine->getMap() );
-            for( unsigned i=0; i<4; ++i )
+            osg::ref_ptr<QuadTreeTerrainEngineNode> engine;
+            QuadTreeTerrainEngineNode::getEngineByUID( (UID)id, engine );
+
+            if ( engine.valid() )
             {
-                TileKey childKey = mapKey.createChildKey( i );
-                if ( !mapf.isCached( childKey ) )
+                const osgEarth::Profile* profile = engine->getMap()->getProfile();
+                osgEarth::TileKey mapKey( lod, x, y, profile );
+
+                result = LOCAL_FILE;
+
+                MapFrame mapf( engine->getMap() );
+                for( unsigned i=0; i<4; ++i )
                 {
-                    result = REMOTE_FILE;
-                    break;
+                    TileKey childKey = mapKey.createChildKey( i );
+                    if ( !mapf.isCached( childKey ) )
+                    {
+                        result = REMOTE_FILE;
+                        break;
+                    }
                 }
             }
+
+            return result;
         }
 
-        return result;
-    }
+        virtual bool useFileCache() const { return false; }
+    };
 
-    virtual bool useFileCache() const { return false; }
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // USE_FILELOCATIONCALLBACK
 
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
index dc933f3..85900fa 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
+++ b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
@@ -25,29 +25,33 @@
 
 using namespace osgEarth;
 
-/**
-* Factory object that can create a scene graph given a TileKey.
-*/
-class KeyNodeFactory : public osg::Referenced
+namespace osgEarth_engine_quadtree
 {
-public:
     /**
-    * Creates a node for a tile key at the root level.
+    * Factory object that can create a scene graph given a TileKey.
     */
-    virtual osg::Node* createRootNode( const TileKey& key ) =0;
+    class KeyNodeFactory : public osg::Referenced
+    {
+    public:
+        /**
+        * Creates a node for a tile key at the root level.
+        */
+        virtual osg::Node* createRootNode( const TileKey& key ) =0;
 
-    /**
-    * Creates a node for a tile key.
-    */
-    virtual osg::Node* createNode( const TileKey& key ) =0;
+        /**
+        * Creates a node for a tile key.
+        */
+        virtual osg::Node* createNode( const TileKey& key ) =0;
+
+        virtual class TileModelCompiler* getCompiler() const =0;
 
-    virtual class TileModelCompiler* getCompiler() const =0;
+    protected:
+        KeyNodeFactory();
 
-protected:
-    KeyNodeFactory();
+        /** dtor */
+        virtual ~KeyNodeFactory() { }
+    };
 
-    /** dtor */
-    virtual ~KeyNodeFactory() { }
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_KEY_NODE_FACTORY
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
index 8ab3fda..fa94232 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
@@ -18,6 +18,7 @@
 */
 #include "KeyNodeFactory"
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 
 #define LC "[KeyNodeFactory] "
diff --git a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback b/src/osgEarthDrivers/engine_quadtree/LODFactorCallback
index 0fdbbe2..4b15d7d 100644
--- a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback
+++ b/src/osgEarthDrivers/engine_quadtree/LODFactorCallback
@@ -22,14 +22,13 @@
 
 #include <osg/NodeCallback>
 
-namespace osgEarth
+namespace osgEarth_engine_quadtree
 {
-    namespace Drivers
+    struct LODFactorCallback : public osg::NodeCallback
     {
-        struct LODFactorCallback : public osg::NodeCallback
-        {
-            void operator()(osg::Node* node, osg::NodeVisitor* nv);
-        };
-    }
-}
+        void operator()(osg::Node* node, osg::NodeVisitor* nv);
+    };
+
+} // namespace osgEarth_engine_quadtree
+
 #endif
diff --git a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp b/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
index 78923fa..04ad128 100644
--- a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
@@ -24,62 +24,57 @@
 #include <osg/Uniform>
 #include <osgUtil/CullVisitor>
 
-namespace osgEarth
+using namespace osgEarth_engine_quadtree;
+
+// This callback sets a uniform, osgearth_LODRangeFactor, based on the
+// distance from the camera and its relation to the minimum and
+// maximum distance for a tile. The maximum distance isn't actually
+// available, so 2 * min distance is used as an estimate. The range
+// factor's value goes from 0 - at the maximum range - to 1 for the
+// minimum range.
+void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
-    namespace Drivers
+    // test the type since this is not always a PagedLOD.
+    osg::PagedLOD* lod = static_cast<osg::PagedLOD*>(node);
+    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
+    osg::LOD::RangeMode rangeMode = lod->getRangeMode();
+    float requiredRange = 0.0f;
+    float rangeFactor = 1.0f;
+    const osg::LOD::RangeList& rangeList = lod->getRangeList();
+    if (rangeMode == osg::LOD::DISTANCE_FROM_EYE_POINT)
     {
-        // This callback sets a uniform, osgearth_LODRangeFactor, based on the
-        // distance from the camera and its relation to the minimum and
-        // maximum distance for a tile. The maximum distance isn't actually
-        // available, so 2 * min distance is used as an estimate. The range
-        // factor's value goes from 0 - at the maximum range - to 1 for the
-        // minimum range.
-
-        void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
+        requiredRange = cv->getDistanceToViewPoint(lod->getCenter(), true);
+    }
+    else if (cv->getLODScale() > 0.0f)
+    {
+        requiredRange = cv->clampedPixelSize(lod->getBound()) / cv->getLODScale();
+    }
+    else
+    {
+        // The comment in osg/PagedLOD.cpp says that this algorithm
+        // finds the highest res tile, but it actually finds the
+        // lowest res tile!
+        for (osg::LOD::RangeList::const_iterator itr = rangeList.begin(), end = rangeList.end();
+            itr != end;
+            ++itr)
         {
-            // test the type since this is not always a PagedLOD.
-            osg::PagedLOD* lod = static_cast<osg::PagedLOD*>(node);
-            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
-            osg::LOD::RangeMode rangeMode = lod->getRangeMode();
-            float requiredRange = 0.0f;
-            float rangeFactor = 1.0f;
-            const osg::LOD::RangeList& rangeList = lod->getRangeList();
-            if (rangeMode == osg::LOD::DISTANCE_FROM_EYE_POINT)
-            {
-                requiredRange = cv->getDistanceToViewPoint(lod->getCenter(), true);
-            }
-            else if (cv->getLODScale() > 0.0f)
-            {
-                requiredRange = cv->clampedPixelSize(lod->getBound()) / cv->getLODScale();
-            }
-            else
-            {
-                // The comment in osg/PagedLOD.cpp says that this algorithm
-                // finds the highest res tile, but it actually finds the
-                // lowest res tile!
-                for (osg::LOD::RangeList::const_iterator itr = rangeList.begin(), end = rangeList.end();
-                    itr != end;
-                    ++itr)
-                {
-                    requiredRange = osg::maximum(requiredRange, itr->first);
-                }
-            }
-            // We're counting on only finding one valid LOD, unlike the
-            // general OSG behavior.
-            if (!rangeList.empty() && rangeList[0].first <= requiredRange
-                && requiredRange < rangeList[0].second)
-            {
-                rangeFactor = 1.0f - (requiredRange - rangeList[0].first) / rangeList[0].first;
-                rangeFactor = osg::clampTo(rangeFactor, 0.0f, 1.0f);
-            }
-            osg::ref_ptr<osg::Uniform> ufact
-                = new osg::Uniform("osgearth_LODRangeFactor", rangeFactor);
-            osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
-            ss->addUniform(ufact.get());
-
-            cv->pushStateSet(ss.get());
-            traverse(node, nv);
-            cv->popStateSet();
+            requiredRange = osg::maximum(requiredRange, itr->first);
         }
     }
+    // We're counting on only finding one valid LOD, unlike the
+    // general OSG behavior.
+    if (!rangeList.empty() && rangeList[0].first <= requiredRange
+        && requiredRange < rangeList[0].second)
+    {
+        rangeFactor = 1.0f - (requiredRange - rangeList[0].first) / rangeList[0].first;
+        rangeFactor = osg::clampTo(rangeFactor, 0.0f, 1.0f);
+    }
+    osg::ref_ptr<osg::Uniform> ufact
+        = new osg::Uniform("osgearth_LODRangeFactor", rangeFactor);
+    osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
+    ss->addUniform(ufact.get());
+
+    cv->pushStateSet(ss.get());
+    traverse(node, nv);
+    cv->popStateSet();
 }
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineNode b/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineNode
deleted file mode 100644
index 3341ce3..0000000
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineNode
+++ /dev/null
@@ -1,138 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
- * http://osgearth.org
- *
- * osgEarth 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
- */
-#ifndef OSGEARTH_ENGINE_QUADTREE_ENGINE_NODE_H
-#define OSGEARTH_ENGINE_QUADTREE_ENGINE_NODE_H 1
-
-#include <osgEarth/TerrainEngineNode>
-#include <osgEarth/TextureCompositor>
-#include <osgEarth/Map>
-#include <osgEarth/Revisioning>
-#include <osgEarth/TaskService>
-
-#include "QuadTreeTerrainEngineOptions"
-#include "OSGTileFactory"
-#include "KeyNodeFactory"
-#include "TileBuilder"
-
-#include <osg/Geode>
-#include <osg/NodeCallback>
-
-using namespace osgEarth;
-
-class QuadTreeTerrainEngineNode : public TerrainEngineNode
-{
-public:
-    QuadTreeTerrainEngineNode();
-    META_Node(osgEarth,QuadTreeTerrainEngineNode);
-    virtual ~QuadTreeTerrainEngineNode();
-
-public:
-    osg::Node* createNode(const TileKey& key);
-
-public: // TerrainEngineNode overrides    
-    virtual void preInitialize( const Map* map, const TerrainOptions& options );
-    virtual void postInitialize( const Map* map, const TerrainOptions& options );
-    virtual void validateTerrainOptions( TerrainOptions& options );
-    virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
-    virtual void traverse( osg::NodeVisitor& );
-    virtual osg::BoundingSphere computeBound() const;    
-
-    // for standalone tile creation outside of a terrain
-    osg::Node* createTile(const TileKey& key);
-
-public: // MapCallback adapter functions
-    void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
-    void onMapModelChanged( const MapModelChange& change ); // not virtual!
-
-    UID getUID() const;
-    OSGTileFactory* getTileFactory() const { return _tileFactory.get(); }
-    class TerrainNode* getTerrainNode() const { return _terrain; }
-
-public: // statics    
-    static void registerEngine( QuadTreeTerrainEngineNode* engineNode );
-    static void unregisterEngine( UID uid );
-    static void getEngineByUID( UID uid, osg::ref_ptr<QuadTreeTerrainEngineNode>& output );
-
-public:
-    class ElevationChangedCallback : public ElevationLayerCallback
-    {
-    public:
-        ElevationChangedCallback( QuadTreeTerrainEngineNode* terrain );
-
-       virtual void onVisibleChanged( TerrainLayer* layer );
-
-        QuadTreeTerrainEngineNode* _terrain;
-        friend class QuadTreeTerrainEngineNode;
-    };
-
-protected:
-	virtual void onVerticalScaleChanged();
-
-private:
-    void init();
-    void syncMapModel();
-    void installTerrainTechnique();
-
-    /**
-     * Reloads all the tiles in the terrain due to a data model change
-     */
-    void refresh();
-
-
-    void addImageLayer( ImageLayer* layer );
-    void addElevationLayer( ElevationLayer* layer );
-
-    void removeImageLayer( ImageLayer* layerRemoved );
-    void removeElevationLayer( ElevationLayer* layerRemoved );
-
-    void moveImageLayer( unsigned int oldIndex, unsigned int newIndex );
-    void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex );
-    
-    void updateElevation( Tile* tile );
-    void installShaders();
-    void updateTextureCombining();
-
-private:
-    osg::ref_ptr<OSGTileFactory>         _tileFactory;
-    //class CustomTerrainNode* _terrain;
-    class TerrainNode*               _terrain;
-    UID                                  _uid;
-    osgEarth::Drivers::OSGTerrainOptions _terrainOptions;
-    Revision                             _shaderLibRev;
-    osg::ref_ptr<TaskServiceManager>     _taskServiceMgr;
-
-    osg::ref_ptr< ElevationChangedCallback > _elevationCallback;
-
-    // store a separate map frame for each of the traversal threads
-    MapFrame* _update_mapf; // map frame for the main/update traversal thread
-    MapFrame* _cull_mapf;   // map frame for the cull traversal thread
-
-    osg::ref_ptr<TaskService>    _tileService;
-    osg::ref_ptr<KeyNodeFactory> _keyNodeFactory;
-    osg::ref_ptr<TileBuilder>    _tileBuilder;
-
-    osg::Timer _timer;
-    unsigned   _tileCount;
-    double     _tileCreationTime;
-    bool       _isStreaming;
-
-    QuadTreeTerrainEngineNode( const QuadTreeTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
-};
-
-#endif // OSGEARTH_ENGINE_QUADTREE_ENGINE_NODE_H
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineNode.cpp b/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineNode.cpp
deleted file mode 100644
index cc959bd..0000000
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineNode.cpp
+++ /dev/null
@@ -1,986 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth 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; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program 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 program.  If not, see <http://www.gnu.org/licenses/>
-*/
-#include "QuadTreeTerrainEngineNode"
-#include "MultiPassTerrainTechnique"
-#include "ParallelKeyNodeFactory"
-#include "SinglePassTerrainTechnique"
-#include "TerrainNode"
-#include "StreamingTerrainNode"
-#include "TileBuilder"
-#include "TransparentLayer"
-
-#include <osgEarth/ImageUtils>
-#include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
-#include <osg/TexEnv>
-#include <osg/TexEnvCombine>
-#include <osg/PagedLOD>
-#include <osg/Timer>
-
-#define LC "[QuadTreeEngine] "
-
-using namespace osgEarth;
-
-//------------------------------------------------------------------------
-
-namespace
-{
-    // adapter that lets QuadTreeTerrainEngineNode listen to Map events
-    struct QuadTreeTerrainEngineNodeMapCallbackProxy : public MapCallback
-    {
-        QuadTreeTerrainEngineNodeMapCallbackProxy(QuadTreeTerrainEngineNode* node) : _node(node) { }
-        osg::observer_ptr<QuadTreeTerrainEngineNode> _node;
-
-        void onMapInfoEstablished( const MapInfo& mapInfo ) {
-            _node->onMapInfoEstablished( mapInfo );
-        }
-
-        void onMapModelChanged( const MapModelChange& change ) {
-            _node->onMapModelChanged( change );
-        }
-    };
-}
-
-//---------------------------------------------------------------------------
-
-//static
-//static OpenThreads::ReentrantMutex s_engineNodeCacheMutex;
-static Threading::ReadWriteMutex s_engineNodeCacheMutex;
-//Caches the MapNodes that have been created
-typedef std::map<UID, osg::observer_ptr<QuadTreeTerrainEngineNode> > EngineNodeCache;
-
-static
-EngineNodeCache& getEngineNodeCache()
-{
-    static EngineNodeCache s_cache;
-    return s_cache;
-}
-
-void
-QuadTreeTerrainEngineNode::registerEngine(QuadTreeTerrainEngineNode* engineNode)
-{
-    Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex );
-    getEngineNodeCache()[engineNode->_uid] = engineNode;
-    OE_DEBUG << LC << "Registered engine " << engineNode->_uid << std::endl;
-}
-
-void
-QuadTreeTerrainEngineNode::unregisterEngine( UID uid )
-{
-    Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex );
-    EngineNodeCache::iterator k = getEngineNodeCache().find( uid );
-    if (k != getEngineNodeCache().end())
-    {
-        getEngineNodeCache().erase(k);
-        OE_DEBUG << LC << "Unregistered engine " << uid << std::endl;
-    }
-}
-
-// since this method is called in a database pager thread, we use a ref_ptr output
-// parameter to avoid the engine node being destructed between the time we 
-// return it and the time it's accessed; this could happen if the user removed the
-// MapNode from the scene during paging.
-void
-QuadTreeTerrainEngineNode::getEngineByUID( UID uid, osg::ref_ptr<QuadTreeTerrainEngineNode>& output )
-{
-    Threading::ScopedReadLock sharedLock( s_engineNodeCacheMutex );
-    EngineNodeCache::const_iterator k = getEngineNodeCache().find( uid );
-    if (k != getEngineNodeCache().end())
-        output = k->second.get();
-}
-
-UID
-QuadTreeTerrainEngineNode::getUID() const
-{
-    return _uid;
-}
-
-//------------------------------------------------------------------------
-
-QuadTreeTerrainEngineNode::ElevationChangedCallback::ElevationChangedCallback( QuadTreeTerrainEngineNode* terrain ):
-_terrain( terrain )
-{
-}
-
-void
-QuadTreeTerrainEngineNode::ElevationChangedCallback::onVisibleChanged( TerrainLayer* layer )
-{    
-    osgEarth::Registry::instance()->clearBlacklist();
-    _terrain->refresh();
-}
-
-//------------------------------------------------------------------------
-
-QuadTreeTerrainEngineNode::QuadTreeTerrainEngineNode() :
-TerrainEngineNode(),
-_terrain         ( 0L ),
-_update_mapf     ( 0L ),
-_cull_mapf       ( 0L ),
-_tileCount       ( 0 ),
-_tileCreationTime( 0.0 )
-{
-    _uid = Registry::instance()->createUID();
-    _taskServiceMgr = Registry::instance()->getTaskServiceManager();
-
-    _elevationCallback = new ElevationChangedCallback( this );
-}
-
-QuadTreeTerrainEngineNode::~QuadTreeTerrainEngineNode()
-{
-    unregisterEngine( _uid );
-
-    if ( _update_mapf )
-    {
-        delete _update_mapf;
-    }
-
-    if ( _cull_mapf )
-    {
-        delete _cull_mapf;
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::preInitialize( const Map* map, const TerrainOptions& options )
-{
-    TerrainEngineNode::preInitialize( map, options );
-
-    _isStreaming =
-        options.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE ||
-        options.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL;
-
-    // in standard mode, try to set the number of OSG DatabasePager threads to use.
-    if ( options.loadingPolicy().isSet() && !_isStreaming )
-    {
-        int numThreads = -1;
-
-        if ( options.loadingPolicy()->numLoadingThreads().isSet() )
-        {
-            numThreads = osg::maximum( 1, *options.loadingPolicy()->numLoadingThreads() );
-        }
-        else if ( options.loadingPolicy()->numLoadingThreadsPerCore().isSet() )
-        {
-            float numThreadsPerCore = *options.loadingPolicy()->numLoadingThreadsPerCore();
-            numThreads = osg::maximum( (int)1, (int)osg::round( 
-                numThreadsPerCore * (float)OpenThreads::GetNumberOfProcessors() ) );
-        }
-
-        if ( numThreads > 0 )
-        {
-            // NOTE: this doesn't work. the pager gets created before we ever get here.
-            numThreads = osg::maximum(numThreads, 2);
-            int numHttpThreads = osg::clampBetween( numThreads/2, 1, numThreads-1 );
-
-            //OE_INFO << LC << "Requesting pager threads in STANDARD mode: local=" << numThreads << ", http=" << numHttpThreads << std::endl;
-            osg::DisplaySettings::instance()->setNumOfDatabaseThreadsHint( numThreads );
-            osg::DisplaySettings::instance()->setNumOfHttpDatabaseThreadsHint( numHttpThreads );
-        }
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options )
-{
-    TerrainEngineNode::postInitialize( map, options );
-
-    // Initialize the map frames. We need one for the update thread and one for the
-    // cull thread. Someday we can detect whether these are actually the same thread
-    // (depends on the viewer's threading mode).
-    _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "osgterrain-update" );
-    _cull_mapf   = new MapFrame( map, Map::TERRAIN_LAYERS, "osgterrain-cull" );
-
-    // merge in the custom options:
-    _terrainOptions.merge( options );
-
-    // handle an already-established map profile:
-    if ( _update_mapf->getProfile() )
-    {
-        // NOTE: this will initialize the map with the startup layers
-        onMapInfoEstablished( MapInfo(map) );
-    }
-
-    // populate the terrain with whatever data is in the map to begin with:
-    if ( _terrain )
-    {
-        // update the terrain revision in threaded mode
-        if ( _isStreaming )
-        {
-            static_cast<StreamingTerrainNode*>(_terrain)->updateTaskServiceThreads( *_update_mapf );
-        }
-
-        updateTextureCombining();
-    }
-
-    // install a layer callback for processing further map actions:
-    map->addMapCallback( new QuadTreeTerrainEngineNodeMapCallbackProxy(this) );
-
-    //Attach to all of the existing elevation layers
-    ElevationLayerVector elevationLayers;
-    map->getElevationLayers( elevationLayers );
-    for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i )
-    {
-        i->get()->addCallback( _elevationCallback.get() );
-    }
-
-    //Attach a callback to all of the 
-
-    // register me.
-    registerEngine( this );
-
-    // now that we have a map, set up to recompute the bounds
-    dirtyBound();
-}
-
-osg::BoundingSphere
-QuadTreeTerrainEngineNode::computeBound() const
-{
-    if ( _terrain && _terrain->getNumChildren() > 0 )
-    {
-        return _terrain->getBound();
-    }
-    else
-    {
-        return TerrainEngineNode::computeBound();
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::refresh()
-{
-    {
-        removeChild( _terrain );
-    }    
-
-
-    _terrain = new TerrainNode(*_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() );    
-    installTerrainTechnique();
-
-   
-    const MapInfo& mapInfo = _update_mapf->getMapInfo();
-    _keyNodeFactory = new SerialKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid );
-
-    // Build the first level of the terrain.
-    // Collect the tile keys comprising the root tiles of the terrain.
-    std::vector< TileKey > keys;
-    _update_mapf->getProfile()->getRootKeys( keys );
-
-    if (_terrainOptions.enableBlending().value())
-    {
-        _terrain->getOrCreateStateSet()->setMode(GL_BLEND , osg::StateAttribute::ON);    
-    }
-
-    addChild( _terrain );
-
-    for( unsigned i=0; i<keys.size(); ++i )
-    {
-        osg::Node* node;
-        if ( _keyNodeFactory.valid() )
-            node = _keyNodeFactory->createRootNode( keys[i] );
-        else
-            node = _tileFactory->createSubTiles( *_update_mapf, _terrain, keys[i], true );
-
-        if ( node )
-            _terrain->addChild( node );
-        else
-            OE_WARN << LC << "Couldn't make tile for root key: " << keys[i].str() << std::endl;
-    }
-
-    updateTextureCombining();
-}
-
-void
-QuadTreeTerrainEngineNode::onMapInfoEstablished( const MapInfo& mapInfo )
-{
-    LoadingPolicy::Mode mode = *_terrainOptions.loadingPolicy()->mode();
-    OE_INFO << LC << "Loading policy mode = " <<
-        ( mode == LoadingPolicy::MODE_PREEMPTIVE ? "PREEMPTIVE" :
-          mode == LoadingPolicy::MODE_SEQUENTIAL ? "SEQUENTIAL" :
-          mode == LoadingPolicy::MODE_PARALLEL   ? "PARALLEL" :
-          "SERIAL/STANDARD" )
-        << std::endl;
-
-    // create a factory for creating actual tile data
-    _tileFactory = new OSGTileFactory( _uid, *_cull_mapf, _terrainOptions );
-
-    // go through and build the root nodesets.
-    if ( !_isStreaming )
-    {
-        _terrain = new TerrainNode(
-            *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() );
-    }
-    else
-    {
-        _terrain = new StreamingTerrainNode(
-            *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() );
-    }
-
-    this->addChild( _terrain );
-
-    // set the initial properties from the options structure:
-    _terrain->setVerticalScale( _terrainOptions.verticalScale().value() );
-    _terrain->setSampleRatio  ( _terrainOptions.heightFieldSampleRatio().value() );
-
-    if (_terrainOptions.enableBlending().value())
-    {
-        _terrain->getOrCreateStateSet()->setMode(GL_BLEND , osg::StateAttribute::ON);    
-    }
-
-    OE_INFO << LC << "Sample ratio = " << _terrainOptions.heightFieldSampleRatio().value() << std::endl;
-
-    // install the proper layer composition technique:
-
-    installTerrainTechnique();    
-
-    // install the shader program, if applicable:
-    installShaders();
-
-    // calculate a good thread pool size for non-streaming parallel processing
-    if ( !_isStreaming )
-    {
-        unsigned num = 2 * OpenThreads::GetNumberOfProcessors();
-        if ( _terrainOptions.loadingPolicy().isSet() )
-        {
-            if ( _terrainOptions.loadingPolicy()->numLoadingThreads().isSet() )
-            {
-                num = *_terrainOptions.loadingPolicy()->numLoadingThreads();
-            }
-            else if ( _terrainOptions.loadingPolicy()->numLoadingThreadsPerCore().isSet() )
-            {
-                num = (unsigned)(*_terrainOptions.loadingPolicy()->numLoadingThreadsPerCore() * OpenThreads::GetNumberOfProcessors());
-            }
-        }
-
-        if ( mode == LoadingPolicy::MODE_PARALLEL )
-        {
-            _tileService = new TaskService( "TileBuilder", num );
-        }
-
-        // initialize the tile builder
-        _tileBuilder = new TileBuilder( getMap(), _terrainOptions, _tileService.get() );
-
-
-        // initialize a key node factory.
-        switch( mode )
-        {
-        case LoadingPolicy::MODE_SERIAL:
-            _keyNodeFactory = new SerialKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid );
-            break;
-
-        case LoadingPolicy::MODE_PARALLEL:
-            _keyNodeFactory = new ParallelKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid );
-            break;
-
-        default:
-            break;
-        }
-    }
-
-    // Build the first level of the terrain.
-    // Collect the tile keys comprising the root tiles of the terrain.
-    std::vector< TileKey > keys;
-    _update_mapf->getProfile()->getRootKeys( keys );
-
-    for( unsigned i=0; i<keys.size(); ++i )
-    {
-        osg::Node* node;
-        if ( _keyNodeFactory.valid() )
-            node = _keyNodeFactory->createRootNode( keys[i] );
-        else
-            node = _tileFactory->createSubTiles( *_update_mapf, _terrain, keys[i], true );
-
-        if ( node )
-            _terrain->addChild( node );
-        else
-            OE_WARN << LC << "Couldn't make tile for root key: " << keys[i].str() << std::endl;
-    }
-
-    // we just added the root tiles, so mark the bound in need of recomputation.
-    dirtyBound();
-}
-
-osg::Node*
-QuadTreeTerrainEngineNode::createNode( const TileKey& key )
-{
-    // if the engine has been disconnected from the scene graph, bail out and don't
-    // create any more tiles
-    if ( getNumParents() == 0 )
-        return 0L;
-
-    OE_DEBUG << LC << "Create node for \"" << key.str() << "\"" << std::endl;
-
-#ifdef PROFILING
-    osg::Timer_t start = _timer.tick();
-#endif
-
-    osg::Node* result = 0L;
-
-    osg::ref_ptr< TerrainNode > terrain = _terrain;
-
-    osg::ref_ptr< KeyNodeFactory > keyNodeFactory = _keyNodeFactory;
-
-    if ( _isStreaming )
-    {
-        // sequential or preemptive mode only.
-        // create a map frame so we can safely create tiles from this dbpager thread
-        MapFrame mapf( getMap(), Map::TERRAIN_LAYERS, "dbpager::earth plugin" );
-        result = getTileFactory()->createSubTiles( mapf, terrain.get(), key, false );
-    }
-    else
-    {
-        if (keyNodeFactory.valid() && terrain.valid())
-        {
-            result = keyNodeFactory->createNode( key );
-        }
-    }
-
-#ifdef PROFILING
-    osg::Timer_t end = osg::Timer::instance()->tick();
-    if ( result )
-    {
-        _tileCount++;
-        _tileCreationTime += _timer.delta_s(start,_timer.tick());
-        if ( _tileCount % 60 == 0 )
-        {
-            OE_INFO << LC << "Avg tile = " << 1000.0*(_tileCreationTime/(double)_tileCount)
-                << " ms, tiles per sec = " << (double)_tileCount/_timer.time_s() << std::endl;
-        }
-    }
-#endif
-
-    return result;
-}
-
-osg::Node*
-QuadTreeTerrainEngineNode::createTile( const TileKey& key )
-{
-    if ( !_tileBuilder.valid() )
-        return 0L;
-
-    osg::ref_ptr<Tile> tile;
-    bool hasRealData, hasLodBlendedLayers;
-
-    _tileBuilder->createTile(
-        key,
-        false,
-        tile,
-        hasRealData,
-        hasLodBlendedLayers );
-
-    if ( !tile.valid() )
-        return 0L;
-
-    // code block required in order to properly manage the ref count of the transform
-    SinglePassTerrainTechnique* tech = new SinglePassTerrainTechnique( _texCompositor.get() );
-    // prepare the interpolation technique for generating triangles:
-    if ( getMap()->getMapOptions().elevationInterpolation() == INTERP_TRIANGULATE )
-        tech->setOptimizeTriangleOrientation( false ); 
-
-    tile->setTerrainTechnique( tech );
-    tile->init();
-    
-    return tech->takeTransform();
-}
-
-void
-QuadTreeTerrainEngineNode::onMapModelChanged( const MapModelChange& change )
-{
-    _update_mapf->sync();
-
-    // dispatch the change handler
-    if ( change.getLayer() )
-    {
-        // first inform the texture compositor with the new model changes:
-        if ( _texCompositor.valid() && change.getImageLayer() )
-        {
-            _texCompositor->applyMapModelChange( change );
-        }
-
-        // then apply the actual change:
-        switch( change.getAction() )
-        {
-        case MapModelChange::ADD_IMAGE_LAYER:
-            addImageLayer( change.getImageLayer() );
-            break;
-        case MapModelChange::REMOVE_IMAGE_LAYER:
-            removeImageLayer( change.getImageLayer() );
-            break;
-        case MapModelChange::ADD_ELEVATION_LAYER:
-            addElevationLayer( change.getElevationLayer() );
-            break;
-        case MapModelChange::REMOVE_ELEVATION_LAYER:
-            removeElevationLayer( change.getElevationLayer() );
-            break;
-        case MapModelChange::MOVE_IMAGE_LAYER:
-            moveImageLayer( change.getFirstIndex(), change.getSecondIndex() );
-            break;
-        case MapModelChange::MOVE_ELEVATION_LAYER:
-            moveElevationLayer( change.getFirstIndex(), change.getSecondIndex() );
-            break;
-        case MapModelChange::ADD_MODEL_LAYER:
-        case MapModelChange::REMOVE_MODEL_LAYER:
-        case MapModelChange::MOVE_MODEL_LAYER:
-        default: break;
-        }
-    }
-
-    // update the terrain revision in threaded mode
-    if ( _isStreaming )
-    {
-        //getTerrain()->incrementRevision();
-        static_cast<StreamingTerrainNode*>(_terrain)->updateTaskServiceThreads( *_update_mapf );
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::addImageLayer( ImageLayer* layerAdded )
-{
-    if ( !layerAdded )
-        return;
-
-    if (!_isStreaming)
-    {
-        refresh();
-    }
-    else
-    {
-        // visit all existing terrain tiles and inform each one of the new image layer:
-        TileVector tiles;
-        _terrain->getTiles( tiles );
-
-        for( TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr )
-        {
-            Tile* tile = itr->get();
-
-            StreamingTile* streamingTile = 0L;
-
-            GeoImage geoImage;
-            bool needToUpdateImagery = false;
-            int imageLOD = -1;
-
-            if ( !_isStreaming || tile->getKey().getLevelOfDetail() == 1 )
-            {
-                // in standard mode, or at the first LOD in seq/pre mode, fetch the image immediately.
-                TileKey geoImageKey = tile->getKey();
-                _tileFactory->createValidGeoImage( layerAdded, tile->getKey(), geoImage, geoImageKey );
-                imageLOD = tile->getKey().getLevelOfDetail();
-            }
-            else
-            {
-                // in seq/pre mode, set up a placeholder and mark the tile as dirty.
-                geoImage = GeoImage(ImageUtils::createEmptyImage(), tile->getKey().getExtent() );
-                needToUpdateImagery = true;
-                streamingTile = static_cast<StreamingTile*>(tile);
-            }
-
-            if (geoImage.valid())
-            {
-                const MapInfo& mapInfo = _update_mapf->getMapInfo();
-
-                double img_min_lon, img_min_lat, img_max_lon, img_max_lat;
-                geoImage.getExtent().getBounds(img_min_lon, img_min_lat, img_max_lon, img_max_lat);
-
-                //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image
-                osg::ref_ptr<GeoLocator> img_locator = tile->getKey().getProfile()->getSRS()->createLocator( 
-                    img_min_lon, img_min_lat, img_max_lon, img_max_lat, 
-                    !mapInfo.isGeocentric() );
-
-                //Set the CS to geocentric if we are dealing with a geocentric map
-                if ( mapInfo.isGeocentric() )
-                {
-                    img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );
-                }
-
-                tile->setCustomColorLayer( CustomColorLayer(
-                    layerAdded,
-                    geoImage.getImage(),
-                    img_locator.get(), imageLOD,  tile->getKey() ) );
-
-                // if necessary, tell the tile to queue up a new imagery request (since we
-                // just installed a placeholder)
-                if ( needToUpdateImagery )
-                {
-                    streamingTile->updateImagery( layerAdded, *_update_mapf, _tileFactory.get() );
-                }
-            }
-            else
-            {
-                // this can happen if there's no data in the new layer for the given tile.
-                // we will rely on the driver to dump out a warning if this is an error.
-            }
-
-            tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() );
-        }
-
-        updateTextureCombining();
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::removeImageLayer( ImageLayer* layerRemoved )
-{
-    if (!_isStreaming)
-    {
-        refresh();
-    }
-    else
-    {
-        // make a thread-safe copy of the tile table
-        TileVector tiles;
-        _terrain->getTiles( tiles );
-
-        for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
-        {
-            Tile* tile = itr->get();
-
-            // critical section
-            tile->removeCustomColorLayer( layerRemoved->getUID() );
-        }
-
-        updateTextureCombining();
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::moveImageLayer( unsigned int oldIndex, unsigned int newIndex )
-{
-    // take a thread-safe copy of the tile table
-    TileVector tiles;
-    _terrain->getTiles( tiles );
-
-    for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
-    {
-        Tile* tile = itr->get();
-        tile->applyImmediateTileUpdate( TileUpdate::MOVE_IMAGE_LAYER );
-    }     
-
-    updateTextureCombining();
-}
-
-void
-QuadTreeTerrainEngineNode::updateElevation( Tile* tile )
-{
-    Threading::ScopedWriteLock exclusiveLock( tile->getTileLayersMutex() );
-
-    const TileKey& key = tile->getKey();
-
-    bool hasElevation = _update_mapf->elevationLayers().size() > 0;
-
-    osgTerrain::HeightFieldLayer* heightFieldLayer = dynamic_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer());
-    if (heightFieldLayer)
-    {
-        // In standard mode, just load the elevation data and dirty the tile.
-        if ( !_isStreaming )
-        {
-            osg::ref_ptr<osg::HeightField> hf;
-
-            if (hasElevation)
-                _update_mapf->getHeightField( key, true, hf, 0L);
-
-            if (!hf.valid()) 
-                hf = OSGTileFactory::createEmptyHeightField( key );
-
-            heightFieldLayer->setHeightField( hf.get() );
-            hf->setSkirtHeight( tile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() );
-
-            //TODO: review this in favor of a tile update...
-            tile->setDirty( true );
-        }
-
-        else // if ( isStreaming )
-        {
-            StreamingTile* stile = static_cast<StreamingTile*>(tile);
-
-            //Update the elevation hint
-            stile->setHasElevationHint( hasElevation );
-
-            //In seq/pre mode, if there is no elevation, just clear out all the elevation on the tiles
-            if ( !hasElevation )
-            {
-                osg::ref_ptr<osg::HeightField> hf = OSGTileFactory::createEmptyHeightField( key );
-                heightFieldLayer->setHeightField( hf.get() );
-                hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() );
-                stile->setElevationLOD( key.getLevelOfDetail() );
-                stile->resetElevationRequests( *_update_mapf );
-                stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION );
-            }
-            else
-            {
-                //Always load the first LOD so the children tiles can have something to use for placeholders
-                if (stile->getKey().getLevelOfDetail() == 1)
-                {
-                    osg::ref_ptr<osg::HeightField> hf;
-                    _update_mapf->getHeightField( key, true, hf, 0L);
-                    if (!hf.valid()) 
-                        hf = OSGTileFactory::createEmptyHeightField( key );
-                    heightFieldLayer->setHeightField( hf.get() );
-                    hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() );
-                    stile->setElevationLOD(tile->getKey().getLevelOfDetail());
-                    stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION );
-                }
-                else
-                {
-                    //Set the elevation LOD to -1
-                    stile->setElevationLOD(-1);
-                    stile->resetElevationRequests( *_update_mapf );
-                }
-            }
-        }
-    }
-}
-
-
-void
-QuadTreeTerrainEngineNode::addElevationLayer( ElevationLayer* layer )
-{
-    if ( !layer )
-        return;
-
-    layer->addCallback( _elevationCallback.get() );
-
-
-    if (!_isStreaming)
-    {
-        refresh();
-    }
-    else
-    {    
-        TileVector tiles;
-        _terrain->getTiles( tiles );
-
-        OE_DEBUG << LC << "Found " << tiles.size() << std::endl;
-
-        for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
-        {
-            updateElevation( itr->get() );
-        }
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::removeElevationLayer( ElevationLayer* layerRemoved )
-{
-    layerRemoved->removeCallback( _elevationCallback.get() );
-
-    if (!_isStreaming)
-    {
-        refresh();
-    }
-    else
-    {
-        TileVector tiles;
-        _terrain->getTiles( tiles );
-
-        for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
-        {
-            updateElevation( itr->get() );
-        }
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::moveElevationLayer( unsigned int oldIndex, unsigned int newIndex )
-{
-    if (!_isStreaming)
-    {
-        refresh();
-    }
-    else
-    {
-        TileVector tiles;
-        _terrain->getTiles( tiles );
-
-        OE_DEBUG << "Found " << tiles.size() << std::endl;
-
-        for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
-        {
-            updateElevation( itr->get() );
-        }
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::validateTerrainOptions( TerrainOptions& options )
-{
-    TerrainEngineNode::validateTerrainOptions( options );
-    
-    //nop for now.
-    //note: to validate plugin-specific features, we would create an OSGTerrainOptions
-    // and do the validation on that. You would then re-integrate it by calling
-    // options.mergeConfig( osgTerrainOptions ).
-}
-
-void
-QuadTreeTerrainEngineNode::traverse( osg::NodeVisitor& nv )
-{
-    if ( _cull_mapf ) // ensures initialize() has been called
-    {
-        if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
-        {
-            // update the cull-thread map frame if necessary. (We don't need to sync the
-            // update_mapf becuase that happens in response to a map callback.)
-
-            // TODO: address the fact that this can happen from multiple threads.
-            // Really we need a _cull_mapf PER view. -gw
-            _cull_mapf->sync();
-        }
-    }
-
-    TerrainEngineNode::traverse( nv );
-}
-
-void
-QuadTreeTerrainEngineNode::installShaders()
-{
-    // This method installs a default shader setup on the engine node itself. The texture compositor
-    // can then override parts of the program by using a VirtualProgram on the _terrain node. We do
-    // it this way so that the developer has the option of removing this top-level shader program,
-    // replacing it, or migrating it higher up the scene graph if necessary.
-
-    if ( _texCompositor.valid() && _texCompositor->usesShaderComposition() )
-    {
-        const ShaderFactory* sf = Registry::instance()->getShaderFactory();
-
-        int numLayers = osg::maximum( 1, (int)_update_mapf->imageLayers().size() );
-
-        VirtualProgram* vp = new VirtualProgram();
-
-        // note. this stuff should probably happen automatically in VirtualProgram. gw
-
-        //vp->setShader( "osgearth_vert_main",     sf->createVertexShaderMain() ); // happens in VirtualProgram now
-        vp->setShader( "osgearth_vert_setupLighting", sf->createDefaultLightingVertexShader() );
-        vp->setShader( "osgearth_vert_setupTexturing",  sf->createDefaultTextureVertexShader( numLayers ) );
-
-        //vp->setShader( "osgearth_frag_main",     sf->createFragmentShaderMain() ); // happend in VirtualProgram now
-        vp->setShader( "osgearth_frag_applyLighting", sf->createDefaultLightingFragmentShader() );
-        vp->setShader( "osgearth_frag_applyTexturing",  sf->createDefaultTextureFragmentShader( numLayers ) );
-
-        getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
-    }
-}
-
-void
-QuadTreeTerrainEngineNode::updateTextureCombining()
-{
-    if ( _texCompositor.valid() )
-    {
-        int numImageLayers = _update_mapf->imageLayers().size();
-        osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet();
-
-        if ( _texCompositor->usesShaderComposition() )
-        {
-            // Creates or updates the shader components that are generated by the texture compositor.
-            // These components reside in the CustomTerrain's stateset, and override the components
-            // installed in the VP on the engine-node's stateset in installShaders().
-
-            VirtualProgram* vp = new VirtualProgram();
-            terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
-
-#if 0
-            VirtualProgram* vp = dynamic_cast<VirtualProgram*>( terrainStateSet->getAttribute(osg::StateAttribute::PROGRAM) );
-            if ( !vp )
-            {
-                // create and add it the first time around..
-                vp = new VirtualProgram();
-                terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
-            }
-#endif
-            
-
-            // first, update the default shader components based on the new layer count:
-            const ShaderFactory* sf = Registry::instance()->getShaderFactory();
-            vp->setShader( "osgearth_vert_setupTexturing",  sf->createDefaultTextureVertexShader( numImageLayers ) );
-            
-            // second, install the per-layer color filter functions.
-            for( int i=0; i<numImageLayers; ++i )
-            {
-                std::string layerFilterFunc = Stringify() << "osgearth_runColorFilters_" << i;
-                const ColorFilterChain& chain = _update_mapf->getImageLayerAt(i)->getColorFilters();
-
-                // install the wrapper function that calls all the filters in turn:
-                vp->setShader( layerFilterFunc, sf->createColorFilterChainFragmentShader(layerFilterFunc, chain) );
-
-                // install each of the filter entry points:
-                for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j )
-                {
-                    const ColorFilter* filter = j->get();
-                    filter->install( terrainStateSet );
-                }
-            }
-
-            // not this one, because the compositor always generates a new one.
-            //vp->setShader( "osgearth_frag_applyTexturing",  lib.createDefaultTextureFragmentShader( numImageLayers ) );
-        }
-
-        // next, inform the compositor that it needs to update based on a new layer count:
-        _texCompositor->updateMasterStateSet( terrainStateSet ); //, numImageLayers );
-    }
-}
-
-namespace
-{
-    class UpdateElevationVisitor : public osg::NodeVisitor
-    {
-    public:
-        UpdateElevationVisitor():
-          osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
-          {}
-
-          void apply(osg::Node& node)
-          {
-              Tile* tile = dynamic_cast<Tile*>(&node);
-              if (tile)
-              {
-                  tile->applyImmediateTileUpdate(TileUpdate::UPDATE_ELEVATION);
-              }
-
-              traverse(node);
-          }
-    };
-}
-
-void
-QuadTreeTerrainEngineNode::onVerticalScaleChanged()
-{
-    _terrain->setVerticalScale(getVerticalScale());
-
-    UpdateElevationVisitor visitor;
-    this->accept(visitor);
-}
-
-void
-QuadTreeTerrainEngineNode::installTerrainTechnique()
-{
-    if ( _texCompositor->getTechnique() == TerrainOptions::COMPOSITING_MULTIPASS )
-    {
-        _terrain->setTechniquePrototype( new MultiPassTerrainTechnique( _texCompositor.get() ) );
-        OE_INFO << LC << "Compositing technique = MULTIPASS" << std::endl;
-    }
-
-    else 
-    {
-        SinglePassTerrainTechnique* tech = new SinglePassTerrainTechnique( _texCompositor.get() );
-        tech->setClearDataAfterCompile( !_isStreaming );
-        
-
-        if ( getMap()->getMapOptions().elevationInterpolation() == INTERP_TRIANGULATE )
-            tech->setOptimizeTriangleOrientation( false );   
-        
-        _terrain->setTechniquePrototype( tech );
-    }
-}
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineOptions b/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineOptions
deleted file mode 100644
index cb31acf..0000000
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeEngineOptions
+++ /dev/null
@@ -1,95 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
- * http://osgearth.org
- *
- * osgEarth 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
- */
-#ifndef OSGEARTH_ENGINE_QUADTREE_OPTIONS
-#define OSGEARTH_ENGINE_QUADTREE_OPTIONS 1
-
-#include <osgEarth/Common>
-#include <osgEarth/TerrainOptions>
-
-namespace osgEarth { namespace Drivers
-{
-    using namespace osgEarth;
-
-    class QuadTreeTerrainEngineOptions : public TerrainOptions // NO EXPORT (header-only)
-    {
-    public:
-        QuadTreeTerrainEngineOptions( const ConfigOptions& options =ConfigOptions() ) : TerrainOptions( options ),
-            _skirtRatio  ( 0.05 ),
-            _quickRelease( true ),
-            _lodFallOff  ( 0.0 )
-        {
-            setDriver( "quadtree" );
-            fromConfig( _conf );
-        }
-
-        /** dtor */
-        virtual ~OSGTerrainOptions() { }
-
-    public:
-        optional<float>& heightFieldSkirtRatio() { return _skirtRatio; }
-        const optional<float>& heightFieldSkirtRatio() const { return _skirtRatio; }
-
-        optional<bool>& quickReleaseGLObjects() { return _quickRelease; }
-        const optional<bool>& quickReleaseGLObjects() const { return _quickRelease; }
-
-        optional<float>& lodFallOff() { return _lodFallOff; }
-        const optional<float>& lodFallOff() const { return _lodFallOff; }
-
-        optional<osg::Node::NodeMask>& surfaceNodeMask() { return _surfaceNodeMask;}
-        const optional<osg::Node::NodeMask>& surfaceNodeMask() const { return _surfaceNodeMask;}
-
-        optional<osg::Node::NodeMask>& skirtNodeMask() { return _skirtNodeMask;}
-        const optional<osg::Node::NodeMask>& skirtNodeMask() const { return _skirtNodeMask;}
-
-
-    protected:
-        virtual Config getConfig() const {
-            Config conf = TerrainOptions::getConfig();
-            conf.updateIfSet( "skirt_ratio", _skirtRatio );
-            conf.updateIfSet( "quick_release_gl_objects", _quickRelease );
-            conf.updateIfSet( "lod_fall_off", _lodFallOff );
-            conf.updateIfSet( "surface_node_mask", _surfaceNodeMask);
-            conf.updateIfSet( "skirt_node_mask", _skirtNodeMask);
-            return conf;
-        }
-
-        virtual void mergeConfig( const Config& conf ) {
-            TerrainOptions::mergeConfig( conf );
-            fromConfig( conf );
-        }
-
-    private:
-        void fromConfig( const Config& conf ) {
-            conf.getIfSet( "skirt_ratio", _skirtRatio );
-            conf.getIfSet( "quick_release_gl_objects", _quickRelease );
-            conf.getIfSet( "lod_fall_off", _lodFallOff );
-            conf.getIfSet( "surface_node_mask", _surfaceNodeMask);
-            conf.getIfSet( "skirt_node_mask", _skirtNodeMask);
-        }
-
-        optional<float> _skirtRatio;
-        optional<bool>  _quickRelease;
-        optional<float> _lodFallOff;
-        optional<osg::Node::NodeMask> _surfaceNodeMask;
-        optional<osg::Node::NodeMask> _skirtNodeMask;
-    };
-
-} } // namespace osgEarth::Drivers
-
-#endif // OSGEARTH_ENGINE_QUADTREE_OPTIONS
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
index d7a169f..a4143e9 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
@@ -27,6 +27,7 @@
 #define LC "[engine_quadtree driver] "
 
 using namespace osgEarth::Drivers;
+using namespace osgEarth_engine_quadtree;
 
 /**
  * osgEarth driver for the QuadTree terrain engine.
@@ -36,10 +37,10 @@ using namespace osgEarth::Drivers;
  * - integrate support for LOD Blending (access to parent state set)
  * - consider TileNodeCompiler cacheing of TexCoord arrays and other tile-shareable data
  */
-class QuadTreeTerrainEngineDriver : public osgDB::ReaderWriter
+class osgEarth_QuadTreeTerrainEngineDriver : public osgDB::ReaderWriter
 {
 public:
-    QuadTreeTerrainEngineDriver() {}
+    osgEarth_QuadTreeTerrainEngineDriver() {}
 
     virtual const char* className()
     {
@@ -145,4 +146,4 @@ public:
     }
 };
 
-REGISTER_OSGPLUGIN(osgearth_engine_quadtree, QuadTreeTerrainEngineDriver)
+REGISTER_OSGPLUGIN(osgearth_engine_quadtree, osgEarth_QuadTreeTerrainEngineDriver)
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
index 69accdf..4a5b50c 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
@@ -37,100 +37,106 @@
 
 using namespace osgEarth;
 
-class QuadTreeTerrainEngineNode : public TerrainEngineNode
+namespace osgEarth_engine_quadtree
 {
-public:
-    QuadTreeTerrainEngineNode();
-    META_Node(osgEarth,QuadTreeTerrainEngineNode);
-    virtual ~QuadTreeTerrainEngineNode();
+    class QuadTreeTerrainEngineNode : public TerrainEngineNode
+    {
+    public:
+        QuadTreeTerrainEngineNode();
+        META_Node(osgEarth,QuadTreeTerrainEngineNode);
+        virtual ~QuadTreeTerrainEngineNode();
 
-public:
-    osg::Node* createNode(const TileKey& key);
+    public:
+        osg::Node* createNode(const TileKey& key);
 
-public: // TerrainEngineNode
+    public: // TerrainEngineNode
 
-    // for standalone tile creation outside of a terrain
-    osg::Node* createTile(const TileKey& key);
+        // for standalone tile creation outside of a terrain
+        osg::Node* createTile(const TileKey& key);
 
-public: // internal TerrainEngineNode
+    public: // internal TerrainEngineNode
 
-    virtual void preInitialize( const Map* map, const TerrainOptions& options );
-    virtual void postInitialize( const Map* map, const TerrainOptions& options );
-    virtual void validateTerrainOptions( TerrainOptions& options );
-    virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
-    virtual osg::BoundingSphere computeBound() const;
+        virtual void preInitialize( const Map* map, const TerrainOptions& options );
+        virtual void postInitialize( const Map* map, const TerrainOptions& options );
+        virtual void validateTerrainOptions( TerrainOptions& options );
+        virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
+        virtual osg::BoundingSphere computeBound() const;
 
-public: // MapCallback adapter functions
-    void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
-    void onMapModelChanged( const MapModelChange& change ); // not virtual!
+    public: // MapCallback adapter functions
+        void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
+        void onMapModelChanged( const MapModelChange& change ); // not virtual!
 
-    UID getUID() const;
+        UID getUID() const;
 
-public: // statics    
-    static void registerEngine( QuadTreeTerrainEngineNode* engineNode );
-    static void unregisterEngine( UID uid );
-    static void getEngineByUID( UID uid, osg::ref_ptr<QuadTreeTerrainEngineNode>& output );
+    public: // statics    
+        static void registerEngine( QuadTreeTerrainEngineNode* engineNode );
+        static void unregisterEngine( UID uid );
+        static void getEngineByUID( UID uid, osg::ref_ptr<QuadTreeTerrainEngineNode>& output );
 
-public:
-    class ElevationChangedCallback : public ElevationLayerCallback
-    {
     public:
-        ElevationChangedCallback( QuadTreeTerrainEngineNode* terrain );
+        class ElevationChangedCallback : public ElevationLayerCallback
+        {
+        public:
+            ElevationChangedCallback( QuadTreeTerrainEngineNode* terrain );
 
-       virtual void onVisibleChanged( TerrainLayer* layer );
+           virtual void onVisibleChanged( TerrainLayer* layer );
 
-        QuadTreeTerrainEngineNode* _terrain;
-        friend class QuadTreeTerrainEngineNode;
-    };
+            QuadTreeTerrainEngineNode* _terrain;
+            friend class QuadTreeTerrainEngineNode;
+        };
+
+    protected:
+        virtual void onVerticalScaleChanged();
 
-protected:
-    virtual void onVerticalScaleChanged();
+    private:
+        void init();
+        void syncMapModel();
 
-private:
-    void init();
-    void syncMapModel();
+        // Reloads all the tiles in the terrain due to a data model change
+        void refresh();
 
-    // Reloads all the tiles in the terrain due to a data model change
-    void refresh();
+        void addImageLayer( ImageLayer* layer );
+        void addElevationLayer( ElevationLayer* layer );
 
-    void addImageLayer( ImageLayer* layer );
-    void addElevationLayer( ElevationLayer* layer );
+        void removeImageLayer( ImageLayer* layerRemoved );
+        void removeElevationLayer( ElevationLayer* layerRemoved );
 
-    void removeImageLayer( ImageLayer* layerRemoved );
-    void removeElevationLayer( ElevationLayer* layerRemoved );
+        void moveImageLayer( unsigned int oldIndex, unsigned int newIndex );
+        void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex );
+        
+        //void updateElevation( TileNode* tile );
+        void installShaders();
+        void updateTextureCombining();
 
-    void moveImageLayer( unsigned int oldIndex, unsigned int newIndex );
-    void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex );
-    
-    //void updateElevation( TileNode* tile );
-    void installShaders();
-    void updateTextureCombining();
+    private:
+        osgEarth::Drivers::QuadTreeTerrainEngineOptions _terrainOptions;
 
-private:
-    osgEarth::Drivers::QuadTreeTerrainEngineOptions _terrainOptions;
+        class TerrainNode* _terrain;
+        UID                _uid;
+        Revision           _shaderLibRev;
 
-    class TerrainNode* _terrain;
-    UID                _uid;
-    Revision           _shaderLibRev;
+        osg::ref_ptr< ElevationChangedCallback > _elevationCallback;
 
-    osg::ref_ptr< ElevationChangedCallback > _elevationCallback;
+        MapFrame* _update_mapf; // map frame for the main/update traversal thread
 
-    MapFrame* _update_mapf; // map frame for the main/update traversal thread
+        // node registry is shared across all threads.
+        osg::ref_ptr<TileNodeRegistry> _liveTiles;      // tiles in the scene graph.
+        osg::ref_ptr<TileNodeRegistry> _deadTiles;        // tiles that used to be in the scene graph.
 
-    // node registry is shared across all threads.
-    osg::ref_ptr<TileNodeRegistry> _liveTiles;      // tiles in the scene graph.
-    osg::ref_ptr<TileNodeRegistry> _deadTiles;        // tiles that used to be in the scene graph.
+        Threading::PerThread< osg::ref_ptr<KeyNodeFactory> > _perThreadKeyNodeFactories;
+        KeyNodeFactory* getKeyNodeFactory();
 
-    Threading::PerThread< osg::ref_ptr<KeyNodeFactory> > _perThreadKeyNodeFactories;
-    KeyNodeFactory* getKeyNodeFactory();
+        osg::Timer _timer;
+        unsigned   _tileCount;
+        double     _tileCreationTime;
 
-    osg::Timer _timer;
-    unsigned   _tileCount;
-    double     _tileCreationTime;
+        osg::Uniform* _verticalScaleUniform;
 
-    osg::Uniform* _verticalScaleUniform;
+        osg::ref_ptr< TileModelFactory > _tileModelFactory;
+
+        QuadTreeTerrainEngineNode( const QuadTreeTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
+    };
 
-    QuadTreeTerrainEngineNode( const QuadTreeTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_ENGINE_NODE_H
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp
index 8b63731..fa55151 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp
@@ -25,7 +25,9 @@
 #include <osgEarth/HeightFieldUtils>
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/ShaderFactory>
+#include <osgEarth/MapModelChange>
 
 #include <osg/TexEnv>
 #include <osg/TexEnvCombine>
@@ -34,6 +36,7 @@
 
 #define LC "[QuadTreeTerrainEngineNode] "
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 
 //------------------------------------------------------------------------
@@ -174,6 +177,10 @@ QuadTreeTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions&
     {
         _deadTiles = new TileNodeRegistry("dead");
     }
+    
+    // initialize the model factory:
+    _tileModelFactory = new TileModelFactory(getMap(), _liveTiles.get(), _terrainOptions );
+
 
     // handle an already-established map profile:
     if ( _update_mapf->getProfile() )
@@ -204,6 +211,8 @@ QuadTreeTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions&
 
     // now that we have a map, set up to recompute the bounds
     dirtyBound();
+
+
 }
 
 
@@ -224,6 +233,13 @@ QuadTreeTerrainEngineNode::computeBound() const
 void
 QuadTreeTerrainEngineNode::refresh()
 {
+
+    //Clear out the hf cache
+    if (_tileModelFactory)
+    {
+        _tileModelFactory->getHeightFieldCache()->clear();
+    }
+
     // rebuilds the terrain graph entirely.
 
     this->removeChild( _terrain );
@@ -311,12 +327,7 @@ QuadTreeTerrainEngineNode::getKeyNodeFactory()
         // create a compiler for compiling tile models into geometry
         bool optimizeTriangleOrientation = 
             getMap()->getMapOptions().elevationInterpolation() != INTERP_TRIANGULATE;
-
-        // initialize the model builder:
-        TileModelFactory* factory = new TileModelFactory(
-            getMap(),
-            _liveTiles.get(),
-            _terrainOptions );
+        
 
         // A compiler specific to this thread:
         TileModelCompiler* compiler = new TileModelCompiler(
@@ -327,7 +338,7 @@ QuadTreeTerrainEngineNode::getKeyNodeFactory()
 
         // initialize a key node factory.
         knf = new SerialKeyNodeFactory( 
-            factory,
+            _tileModelFactory.get(),
             compiler,
             _liveTiles.get(),
             _deadTiles.get(),
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions
index ff00379..37b25a4 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions
@@ -32,7 +32,8 @@ namespace osgEarth { namespace Drivers
         QuadTreeTerrainEngineOptions( const ConfigOptions& options =ConfigOptions() ) : TerrainOptions( options ),
             _skirtRatio  ( 0.05 ),
             _quickRelease( true ),
-            _lodFallOff  ( 0.0 )
+            _lodFallOff  ( 0.0 ),
+            _normalizeEdges( false )
         {
             setDriver( "quadtree" );
             fromConfig( _conf );
@@ -51,6 +52,9 @@ namespace osgEarth { namespace Drivers
         optional<float>& lodFallOff() { return _lodFallOff; }
         const optional<float>& lodFallOff() const { return _lodFallOff; }
 
+        optional<bool>& normalizeEdges() { return _normalizeEdges; }
+        const optional<bool>& normalizeEdges() const { return _normalizeEdges; }
+
 
     protected:
         virtual Config getConfig() const {
@@ -58,6 +62,7 @@ namespace osgEarth { namespace Drivers
             conf.updateIfSet( "skirt_ratio", _skirtRatio );
             conf.updateIfSet( "quick_release_gl_objects", _quickRelease );
             conf.updateIfSet( "lod_fall_off", _lodFallOff );
+            conf.updateIfSet( "normalize_edges", _normalizeEdges);
             return conf;
         }
 
@@ -71,11 +76,13 @@ namespace osgEarth { namespace Drivers
             conf.getIfSet( "skirt_ratio", _skirtRatio );
             conf.getIfSet( "quick_release_gl_objects", _quickRelease );
             conf.getIfSet( "lod_fall_off", _lodFallOff );
+            conf.getIfSet( "normalize_edges", _normalizeEdges );
         }
 
         optional<float> _skirtRatio;
         optional<bool>  _quickRelease;
         optional<float> _lodFallOff;
+        optional<bool> _normalizeEdges;
     };
 
 } } // namespace osgEarth::Drivers
diff --git a/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects b/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
index dc58340..9a32417 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
+++ b/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
@@ -23,7 +23,7 @@
 #include "TileNodeRegistry"
 #include <osg/Camera>
 
-namespace
+namespace osgEarth_engine_quadtree
 {
     /**
      * A draw callback to calls another, nested draw callback.
@@ -85,6 +85,7 @@ namespace
 
         osg::ref_ptr<TileNodeRegistry> _tilesToRelease;
     };
-}
+
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_QUICK_RELEASE_GL_OBJECTS
diff --git a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
index 88a3f93..954c602 100644
--- a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
@@ -24,46 +24,51 @@
 #include "TerrainNode"
 #include "TileModelFactory"
 #include "TileNodeRegistry"
+#include <osgEarth/MapInfo>
 
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 
-class SerialKeyNodeFactory : public KeyNodeFactory
+namespace osgEarth_engine_quadtree
 {
-public:
-    SerialKeyNodeFactory(
-        TileModelFactory*                   modelFactory,
-        TileModelCompiler*                  modelCompiler,
-        TileNodeRegistry*                   liveTiles,
-        TileNodeRegistry*                   deadTiles,
-        const QuadTreeTerrainEngineOptions& options,
-        const MapInfo&                      mapInfo,
-        TerrainNode*                        terrain,
-        UID                                 engineUID );
+    class SerialKeyNodeFactory : public KeyNodeFactory
+    {
+    public:
+        SerialKeyNodeFactory(
+            TileModelFactory*                   modelFactory,
+            TileModelCompiler*                  modelCompiler,
+            TileNodeRegistry*                   liveTiles,
+            TileNodeRegistry*                   deadTiles,
+            const QuadTreeTerrainEngineOptions& options,
+            const MapInfo&                      mapInfo,
+            TerrainNode*                        terrain,
+            UID                                 engineUID );
 
-    /** dtor */
-    virtual ~SerialKeyNodeFactory() { }
+        /** dtor */
+        virtual ~SerialKeyNodeFactory() { }
 
 
-public: // KeyNodeFactory
+    public: // KeyNodeFactory
 
-    osg::Node* createRootNode( const TileKey& key );
+        osg::Node* createRootNode( const TileKey& key );
 
-    osg::Node* createNode( const TileKey& key );
+        osg::Node* createNode( const TileKey& key );
 
-    TileModelCompiler* getCompiler() const { return _modelCompiler.get(); }
+        TileModelCompiler* getCompiler() const { return _modelCompiler.get(); }
 
-protected:
-    void addTile(TileModel* model, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent );
+    protected:
+        void addTile(TileModel* model, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent );
 
-    osg::ref_ptr<TileModelFactory>      _modelFactory;
-    osg::ref_ptr<TileModelCompiler>     _modelCompiler;
-    osg::ref_ptr<TileNodeRegistry>      _liveTiles;
-    osg::ref_ptr<TileNodeRegistry>      _deadTiles;
-    const QuadTreeTerrainEngineOptions& _options;
-    const MapInfo                       _mapInfo;
-    osg::ref_ptr< TerrainNode >         _terrain;
-    UID                                 _engineUID;
-};
+        osg::ref_ptr<TileModelFactory>      _modelFactory;
+        osg::ref_ptr<TileModelCompiler>     _modelCompiler;
+        osg::ref_ptr<TileNodeRegistry>      _liveTiles;
+        osg::ref_ptr<TileNodeRegistry>      _deadTiles;
+        const QuadTreeTerrainEngineOptions& _options;
+        const MapInfo                       _mapInfo;
+        osg::ref_ptr< TerrainNode >         _terrain;
+        UID                                 _engineUID;
+    };
+
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_SERIAL_KEY_NODE_FACTORY
diff --git a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
index a760491..082705b 100644
--- a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
@@ -30,6 +30,7 @@
 
 #include <osgEarth/MapNode>
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 using namespace OpenThreads;
 
@@ -126,7 +127,7 @@ SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileH
         {
             // Make the LOD transition distance, and a measure of how
             // close the tile is to an LOD change, to shaders.
-            result->addCullCallback(new Drivers::LODFactorCallback);
+            result->addCullCallback(new LODFactorCallback);
         }
     }
     else
diff --git a/src/osgEarthDrivers/engine_quadtree/TerrainNode b/src/osgEarthDrivers/engine_quadtree/TerrainNode
index 15fafa4..b9243ce 100644
--- a/src/osgEarthDrivers/engine_quadtree/TerrainNode
+++ b/src/osgEarthDrivers/engine_quadtree/TerrainNode
@@ -22,35 +22,39 @@
 #include "Common"
 #include "TileNodeRegistry"
 
-class TileFactory;
-
-using namespace osgEarth;
-using namespace osgEarth::Drivers;
-
-/**
- * Parent node for the TileNode quadtree hierarchy.
- */
-class TerrainNode : public osg::Group
+namespace osgEarth_engine_quadtree
 {
-public:
+    class TileFactory;
+
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
 
     /**
-     * Constructs a new terrain node.
-     * @param[in ] deadTiles If non-NULL, the terrain node will active GL object
-     *             quick-release and use this registry to track dead tiles.
+     * Parent node for the TileNode quadtree hierarchy.
      */
-    TerrainNode( TileNodeRegistry* deadTiles );
+    class TerrainNode : public osg::Group
+    {
+    public:
+
+        /**
+         * Constructs a new terrain node.
+         * @param[in ] deadTiles If non-NULL, the terrain node will active GL object
+         *             quick-release and use this registry to track dead tiles.
+         */
+        TerrainNode( TileNodeRegistry* deadTiles );
+
+    public: // osg::Node
 
-public: // osg::Node
+        virtual void traverse( osg::NodeVisitor &nv );
 
-    virtual void traverse( osg::NodeVisitor &nv );
+    protected:
 
-protected:
+        virtual ~TerrainNode() { }
 
-    virtual ~TerrainNode() { }
+        osg::ref_ptr<TileNodeRegistry> _tilesToQuickRelease;
+        bool _quickReleaseCallbackInstalled;
+    };
 
-    osg::ref_ptr<TileNodeRegistry> _tilesToQuickRelease;
-    bool _quickReleaseCallbackInstalled;
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_TERRAIN_NODE
diff --git a/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp b/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
index 1edcb97..143c200 100644
--- a/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
@@ -29,6 +29,7 @@
 #include <osg/Node>
 #include <osgGA/EventVisitor>
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModel b/src/osgEarthDrivers/engine_quadtree/TileModel
index 56f68f0..cc093bc 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModel
+++ b/src/osgEarthDrivers/engine_quadtree/TileModel
@@ -30,131 +30,165 @@
 #include <osg/StateSet>
 #include <map>
 
-using namespace osgEarth;
-
-
-class TileModel : public osg::Referenced
+namespace osgEarth_engine_quadtree
 {
-public:
-    class ElevationData
-    {
-    public:
-        ElevationData() { }
-        virtual ~ElevationData() { }
-        ElevationData( osgTerrain::HeightFieldLayer* hfLayer, bool fallbackData =false )
-            : _hfLayer(hfLayer), _fallbackData(fallbackData) { }
-
-        osgTerrain::HeightFieldLayer* getHFLayer() const { return _hfLayer.get(); }
-        bool isFallbackData() const { return _fallbackData; }
-
-    private:
-        osg::ref_ptr<osgTerrain::HeightFieldLayer> _hfLayer;
-        bool _fallbackData;
-    };
+    using namespace osgEarth;
 
-
-    class ColorData
+    class TileModel : public osg::Referenced
     {
     public:
-        ColorData() { }
-
-        /** dtor */
-        virtual ~ColorData() { }
-
-        ColorData(
-            const osgEarth::ImageLayer* imageLayer,
-            osg::Image* image,
-            const osgTerrain::Locator* locator,
-            int lod,
-            const osgEarth::TileKey& tileKey,
-            bool fallbackData =false )
-            : _layer(imageLayer), _locator(locator), _image(image),  _tileKey(tileKey), _lod(lod), _fallbackData(fallbackData) { }
-
-        ColorData( const ColorData& rhs ) :
-            _layer( rhs._layer.get() ),        
-            _locator( rhs._locator.get() ),
-            _image( rhs._image.get() ),
-            _tileKey( rhs._tileKey ),
-            _lod( rhs._lod ),
-            _fallbackData( rhs._fallbackData ) { }
-
-        osgEarth::UID getUID() const {
-            return _layer->getUID();
-        }
+        // do not change the order of these.
+        enum Neighbor
+        {
+            NORTHWEST = 0,
+            NORTH     = 1,
+            NORTHEAST = 2,
+            WEST      = 3,
+            EAST      = 4,
+            SOUTHWEST = 5,
+            SOUTH     = 6,
+            SOUTHEAST = 7
+        };
+
+        class ElevationData
+        {
+        public:
+            ElevationData() { }
+            virtual ~ElevationData() { }
+            ElevationData( osgTerrain::HeightFieldLayer* hfLayer, bool fallbackData =false )
+                : _hfLayer(hfLayer), _fallbackData(fallbackData) { }
+
+            osgTerrain::HeightFieldLayer* getHFLayer() const { return _hfLayer.get(); }
+            bool isFallbackData() const { return _fallbackData; }
+            
+            osg::HeightField* getNeighbor(int xoffset, int yoffset) const
+            {
+                int index = getNeighborIndex(xoffset, yoffset);
+                return _neighbors[index];
+            }
 
-        const osgTerrain::Locator* getLocator() const {
-            return _locator.get();
-        }
+            void setNeighbor(int xoffset, int yoffset, osg::HeightField* hf )
+            {
+                int index = getNeighborIndex(xoffset, yoffset);
+                _neighbors[index] = hf;
+            }
 
-        osg::Image* getImage() const { 
-            return _image.get(); }
+        private:
+            static int getNeighborIndex(int xoffset, int yoffset)
+            {
+                int index = 3*(yoffset+1)+(xoffset+1);
+                if (index > 4) index--;
+                return index;
+            }
+            osg::ref_ptr<osgTerrain::HeightFieldLayer> _hfLayer;
+            bool _fallbackData;
+            osg::ref_ptr<osg::HeightField> _neighbors[8];
+        };
+
+
+        class ColorData
+        {
+        public:
+            ColorData() { }
+
+            /** dtor */
+            virtual ~ColorData() { }
+
+            ColorData(
+                const osgEarth::ImageLayer* imageLayer,
+                osg::Image* image,
+                const osgTerrain::Locator* locator,
+                int lod,
+                const osgEarth::TileKey& tileKey,
+                bool fallbackData =false )
+                : _layer(imageLayer), _locator(locator), _image(image),  _tileKey(tileKey), _lod(lod), _fallbackData(fallbackData) { }
+
+            ColorData( const ColorData& rhs ) :
+                _layer( rhs._layer.get() ),        
+                _locator( rhs._locator.get() ),
+                _image( rhs._image.get() ),
+                _tileKey( rhs._tileKey ),
+                _lod( rhs._lod ),
+                _fallbackData( rhs._fallbackData ) { }
+
+            osgEarth::UID getUID() const {
+                return _layer->getUID();
+            }
 
-        const osgEarth::TileKey& getTileKey() const {
-            return _tileKey; }
+            const osgTerrain::Locator* getLocator() const {
+                return _locator.get();
+            }
 
-        const osgEarth::ImageLayer* getMapLayer() const {
-            return _layer.get(); }
+            osg::Image* getImage() const { 
+                return _image.get(); }
 
-        int getLevelOfDetail() const {
-            return _lod; }
+            const osgEarth::TileKey& getTileKey() const {
+                return _tileKey; }
 
-        bool isFallbackData() const {
-            return _fallbackData; }
+            const osgEarth::ImageLayer* getMapLayer() const {
+                return _layer.get(); }
 
-        osg::BoundingSphere computeBound() const {
-            osg::BoundingSphere bs;
-            osg::Vec3d v;
-            if (getLocator()->convertLocalToModel(osg::Vec3d(0.5,0.5,0.0), v)) {
-                bs.center() = v;
-            }
-            if (getLocator()->convertLocalToModel(osg::Vec3d(0.0,0.0,0.0), v)) {
-                bs.radius() = (bs.center() - v).length();
-            }
-            return bs;
-        }
+            int getLevelOfDetail() const {
+                return _lod; }
 
+            bool isFallbackData() const {
+                return _fallbackData; }
 
-    private:
-        osg::ref_ptr<const osgEarth::ImageLayer> _layer;
-        osg::ref_ptr<const osgTerrain::Locator>  _locator;
-        osg::ref_ptr<osg::Image>                 _image;
-        osgEarth::TileKey                        _tileKey;
-        int                                      _lod;
-        bool                                     _fallbackData;
-    };
+            osg::BoundingSphere computeBound() const {
+                osg::BoundingSphere bs;
+                osg::Vec3d v;
+                if (getLocator()->convertLocalToModel(osg::Vec3d(0.5,0.5,0.0), v)) {
+                    bs.center() = v;
+                }
+                if (getLocator()->convertLocalToModel(osg::Vec3d(0.0,0.0,0.0), v)) {
+                    bs.radius() = (bs.center() - v).length();
+                }
+                return bs;
+            }
 
-    class ColorDataRef : public osg::Referenced
-    {
-    public:
-        ColorDataRef( const ColorData& layer ) : _layer(layer) { }
-        ColorData _layer;
-    };
 
+        private:
+            osg::ref_ptr<const osgEarth::ImageLayer> _layer;
+            osg::ref_ptr<const osgTerrain::Locator>  _locator;
+            osg::ref_ptr<osg::Image>                 _image;
+            osgEarth::TileKey                        _tileKey;
+            int                                      _lod;
+            bool                                     _fallbackData;
+        };
 
-    typedef std::map<UID, ColorData> ColorDataByUID;
+        class ColorDataRef : public osg::Referenced
+        {
+        public:
+            ColorDataRef( const ColorData& layer ) : _layer(layer) { }
+            ColorData _layer;
+        };
 
 
-public:
-    TileModel() { }
-    virtual ~TileModel() { }
+        typedef std::map<UID, ColorData> ColorDataByUID;
 
-    TileKey                     _tileKey;
-    osg::ref_ptr<GeoLocator>    _tileLocator;
-    ColorDataByUID              _colorData;
-    ElevationData               _elevationData;
-    float                       _sampleRatio;
-    osg::ref_ptr<osg::StateSet> _parentStateSet;
 
-    // convenience funciton to pull out a layer by its UID.
-    bool getColorData( UID layerUID, ColorData& out ) const {
-        ColorDataByUID::const_iterator i = _colorData.find( layerUID );
-        if ( i != _colorData.end() ) {
-            out = i->second;
-            return true;
+    public:
+        TileModel() { }
+        virtual ~TileModel() { }
+
+        TileKey                     _tileKey;
+        osg::ref_ptr<GeoLocator>    _tileLocator;
+        ColorDataByUID              _colorData;
+        ElevationData               _elevationData;
+        float                       _sampleRatio;
+        osg::ref_ptr<osg::StateSet> _parentStateSet;
+
+        // convenience funciton to pull out a layer by its UID.
+        bool getColorData( UID layerUID, ColorData& out ) const {
+            ColorDataByUID::const_iterator i = _colorData.find( layerUID );
+            if ( i != _colorData.end() ) {
+                out = i->second;
+                return true;
+            }
+            return false;
         }
-        return false;
-    }
-};
+    };
 
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
index 293946c..ba5b1ef 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
@@ -33,68 +33,72 @@
 #include <osg/Drawable>
 #include <osg/Array>
 
-using namespace osgEarth;
-using namespace osgEarth::Drivers;
-
-/**
- * Cache used by the TileModelCompiler. This lets us share common data across
- * compilations. Since the compiler is thread-locked, we just have a separate
- * cache per compiler.
- *
- * Important Note! Any array you store in the cache MUST have it's OWN 
- * unique VBO. Call array->setVertexBufferObject( new osg::VertexBufferObject() )
- * to assign one. This will prevent non-thread-safe buffer object sharing.
- */
-struct CompilerCache
+namespace osgEarth_engine_quadtree
 {
-    // Texture coordinate array cache def
-    struct TexCoordTableKey {
-        osg::ref_ptr<const GeoLocator> _locator;
-        osg::Vec4d                     _mat;
-        unsigned                       _cols, _rows;
-    };
-    typedef std::pair< TexCoordTableKey, osg::ref_ptr<osg::Vec2Array> > LocatorTexCoordPair;
-    struct TexCoordArrayCache : public std::list<LocatorTexCoordPair> {
-        osg::ref_ptr<osg::Vec2Array>& get( const osg::Vec4d& mat, unsigned cols, unsigned rows );
-    };
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
 
-    TexCoordArrayCache _surfaceTexCoordArrays;
-    TexCoordArrayCache _skirtTexCoordArrays;
-};
+    /**
+     * Cache used by the TileModelCompiler. This lets us share common data across
+     * compilations. Since the compiler is thread-locked, we just have a separate
+     * cache per compiler.
+     *
+     * Important Note! Any array you store in the cache MUST have it's OWN 
+     * unique VBO. Call array->setVertexBufferObject( new osg::VertexBufferObject() )
+     * to assign one. This will prevent non-thread-safe buffer object sharing.
+     */
+    struct CompilerCache
+    {
+        // Texture coordinate array cache def
+        struct TexCoordTableKey {
+            osg::ref_ptr<const GeoLocator> _locator;
+            osg::Vec4d                     _mat;
+            unsigned                       _cols, _rows;
+        };
+        typedef std::pair< TexCoordTableKey, osg::ref_ptr<osg::Vec2Array> > LocatorTexCoordPair;
+        struct TexCoordArrayCache : public std::list<LocatorTexCoordPair> {
+            osg::ref_ptr<osg::Vec2Array>& get( const osg::Vec4d& mat, unsigned cols, unsigned rows );
+        };
 
+        TexCoordArrayCache _surfaceTexCoordArrays;
+        TexCoordArrayCache _skirtTexCoordArrays;
+    };
 
-/**
- * Builds the actual tile geometry.
- *
- * When used with the KeyNodeFactory, there will be exactly one instance of this
- * class per thread. So, we can expand this to include caches for commonly shared
- * data like texture coordinate or color arrays.
- */
-class TileModelCompiler : public osg::Referenced
-{
-public:
-    TileModelCompiler(
-        const MaskLayerVector&              masks,
-        TextureCompositor*                  compositor,
-        bool                                optimizeTriangleOrientation,
-        const QuadTreeTerrainEngineOptions& options);
 
     /**
-     * Compiles a tile model into an OSG scene graph. The scene graph will
-     * include a MatrixTransform to localize the tile data.
+     * Builds the actual tile geometry.
+     *
+     * When used with the KeyNodeFactory, there will be exactly one instance of this
+     * class per thread. So, we can expand this to include caches for commonly shared
+     * data like texture coordinate or color arrays.
      */
-    bool compile(
-        const TileModel* model,
-        osg::Node*&      out_node,
-        osg::StateSet*&  out_stateSet );
+    class TileModelCompiler : public osg::Referenced
+    {
+    public:
+        TileModelCompiler(
+            const MaskLayerVector&              masks,
+            TextureCompositor*                  compositor,
+            bool                                optimizeTriangleOrientation,
+            const QuadTreeTerrainEngineOptions& options);
+
+        /**
+         * Compiles a tile model into an OSG scene graph. The scene graph will
+         * include a MatrixTransform to localize the tile data.
+         */
+        bool compile(
+            const TileModel* model,
+            osg::Node*&      out_node,
+            osg::StateSet*&  out_stateSet );
+
+    protected:
+        const MaskLayerVector&                    _masks;
+        osg::ref_ptr<TextureCompositor>           _texCompositor;
+        bool                                      _optimizeTriOrientation;
+        const QuadTreeTerrainEngineOptions&       _options;
+        osg::ref_ptr<osg::Drawable::CullCallback> _cullByTraversalMask;
+        CompilerCache                             _cache;
+    };
 
-protected:
-    const MaskLayerVector&                    _masks;
-    osg::ref_ptr<TextureCompositor>           _texCompositor;
-    bool                                      _optimizeTriOrientation;
-    const QuadTreeTerrainEngineOptions&       _options;
-    osg::ref_ptr<osg::Drawable::CullCallback> _cullByTraversalMask;
-    CompilerCache                             _cache;
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_COMPILER
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
index 9b90d64..86221d4 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
@@ -28,6 +28,7 @@
 #include <osg/MatrixTransform>
 #include <osgUtil/DelaunayTriangulator>
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 using namespace osgEarth::Symbology;
@@ -1192,7 +1193,7 @@ namespace
      * Builds triangles for the surface geometry, and recalculates the surface normals
      * to be optimized for slope.
      */
-    void tessellateSurfaceGeometry( Data& d, bool optimizeTriangleOrientation )
+    void tessellateSurfaceGeometry( Data& d, bool optimizeTriangleOrientation, bool normalizeEdges )
     {    
         bool swapOrientation = !(d.model->_tileLocator->orientationOpenGL());
         bool recalcNormals   = d.model->_elevationData.getHFLayer() != 0L;
@@ -1249,7 +1250,7 @@ namespace
                 if (i00>=0) ++numValid;
                 if (i01>=0) ++numValid;
                 if (i10>=0) ++numValid;
-                if (i11>=0) ++numValid;
+                if (i11>=0) ++numValid;                
 
                 if (numValid==4)
                 {
@@ -1331,13 +1332,358 @@ namespace
             }
         }
 
-        // Finally, normalize the normals.
-        if ( recalcNormals )
+        
+        if (recalcNormals && normalizeEdges)
+        {
+            OE_DEBUG << "Normalizing edges" << std::endl;
+            //Compute the edge normals if we have neighbor data
+            //Get all the neighbors
+            osg::ref_ptr< osg::HeightField > w_neighbor  = d.model->_elevationData.getNeighbor( -1, 0 );
+            osg::ref_ptr< osg::HeightField > e_neighbor  = d.model->_elevationData.getNeighbor( 1, 0 );            
+            osg::ref_ptr< osg::HeightField > s_neighbor  = d.model->_elevationData.getNeighbor( 0, 1 );
+            osg::ref_ptr< osg::HeightField > n_neighbor  = d.model->_elevationData.getNeighbor( 0, -1 );            
+
+            //Recalculate the west side
+            if (w_neighbor.valid() && w_neighbor->getNumColumns() == d.numCols && w_neighbor->getNumRows() == d.numRows)
+            {                                     
+                osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
+                boundaryVerts->reserve( 2 * d.numRows );
+
+                std::vector< float > boundaryElevations;
+                boundaryElevations.reserve( 2 * d.numRows );
+
+                //Compute the verts for the west side
+                for (int j = 0; j < (int)d.numRows; j++)
+                {
+                    for (int i = (int)d.numCols-2; i <= (int)d.numCols-1; i++)
+                    {                          
+                        osg::Vec3d ndc( (double)(i - static_cast<int>(d.numCols-1))/(double)(d.numCols-1), ((double)j)/(double)(d.numRows-1), 0.0);                                                                        
+
+                        //TODO:  Should probably use an interpolated method here
+                        float heightValue = w_neighbor->getHeight( i, j );
+                        ndc.z() = heightValue;                        
+
+                        osg::Vec3d model;
+                        d.model->_tileLocator->unitToModel( ndc, model );
+                        osg::Vec3d v = model - d.centerModel;
+                        boundaryVerts->push_back( v );
+                        boundaryElevations.push_back( heightValue );                        
+                    }
+                }   
+
+                //The boundary verts are now populated, so go through and triangulate them add add the normals to the existing normal array
+                for (int j = 0; j < (int)d.numRows-1; j++)
+                {                    
+                    int i00;
+                    int i01;
+                    int i = 0;
+                    if (swapOrientation)
+                    {
+                        i01 = j*d.numCols + i;
+                        i00 = i01+d.numCols;
+                    }
+                    else
+                    {
+                        i00 = j*d.numCols + i;
+                        i01 = i00+d.numCols;
+                    }
+
+
+
+                    //remap indices to final vertex position
+                    i00 = d.indices[i00];
+                    i01 = d.indices[i01];
+
+                    int baseIndex = 2 * j;
+                    osg::Vec3f& v00 = (*boundaryVerts)[baseIndex    ];
+                    osg::Vec3f& v10 = (*boundaryVerts)[baseIndex + 1];
+                    osg::Vec3f& v01 = (*boundaryVerts)[baseIndex + 2];
+                    osg::Vec3f& v11 = (*boundaryVerts)[baseIndex + 3];
+
+                    float e00 = boundaryElevations[baseIndex];
+                    float e10 = boundaryElevations[baseIndex + 1];
+                    float e01 = boundaryElevations[baseIndex + 2];
+                    float e11 = boundaryElevations[baseIndex + 3];
+
+                   
+                    if (!optimizeTriangleOrientation || (e00-e11)<fabsf(e01-e10))
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v11-v01);
+                        (*d.normals)[i01] += normal1;                        
+
+                        osg::Vec3 normal2 = (v10-v00) ^ (v11-v00);
+                        (*d.normals)[i00] += normal2;                        
+                        (*d.normals)[i01] += normal2;                                                
+                    }
+                    else
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v10-v01);
+                        (*d.normals)[i00] += normal1;                                               
+
+                        osg::Vec3 normal2 = (v10-v01) ^ (v11-v01);
+                        (*d.normals)[i00] += normal2;                                               
+                        (*d.normals)[i01] += normal2;                        
+                    }
+                }
+            }
+
+                        
+            //Recalculate the east side
+            if (e_neighbor.valid() && e_neighbor->getNumColumns() == d.numCols && e_neighbor->getNumRows() == d.numRows)
+            {                           
+                osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
+                boundaryVerts->reserve( 2 * d.numRows );
+
+                std::vector< float > boundaryElevations;
+                boundaryElevations.reserve( 2 * d.numRows );
+
+                //Compute the verts for the east side
+                for (int j = 0; j < (int)d.numRows; j++)
+                {
+                    for (int i = 0; i <= 1; i++)
+                    {                           
+                        osg::Vec3d ndc( ((double)(d.numCols -1 + i))/(double)(d.numCols-1), ((double)j)/(double)(d.numRows-1), 0.0);
+                        
+                        //TODO:  Should probably use an interpolated method here
+                        float heightValue = e_neighbor->getHeight( i, j );
+                        ndc.z() = heightValue;                        
+
+                        osg::Vec3d model;
+                        d.model->_tileLocator->unitToModel( ndc, model );
+                        osg::Vec3d v = model - d.centerModel;
+                        boundaryVerts->push_back( v );
+                        boundaryElevations.push_back( heightValue );                        
+                    }
+                }   
+
+                //The boundary verts are now populated, so go through and triangulate them add add the normals to the existing normal array
+                for (int j = 0; j < (int)d.numRows-1; j++)
+                {                    
+                    int i00;
+                    int i01;
+                    int i = d.numCols-1;
+                    if (swapOrientation)
+                    {
+                        i01 = j*d.numCols + i;
+                        i00 = i01+d.numCols;
+                    }
+                    else
+                    {
+                        i00 = j*d.numCols + i;
+                        i01 = i00+d.numCols;
+                    }
+
+                    //remap indices to final vertex position
+                    i00 = d.indices[i00];
+                    i01 = d.indices[i01];
+
+                    int baseIndex = 2 * j;
+                    osg::Vec3f& v00 = (*boundaryVerts)[baseIndex    ];
+                    osg::Vec3f& v10 = (*boundaryVerts)[baseIndex + 1];
+                    osg::Vec3f& v01 = (*boundaryVerts)[baseIndex + 2];
+                    osg::Vec3f& v11 = (*boundaryVerts)[baseIndex + 3];
+
+                    float e00 = boundaryElevations[baseIndex];
+                    float e10 = boundaryElevations[baseIndex + 1];
+                    float e01 = boundaryElevations[baseIndex + 2];
+                    float e11 = boundaryElevations[baseIndex + 3];
+
+                   
+                    if (!optimizeTriangleOrientation || (e00-e11)<fabsf(e01-e10))
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v11-v01);                       
+                        (*d.normals)[i00] += normal1;                        
+                        (*d.normals)[i01] += normal1;
+
+                        osg::Vec3 normal2 = (v10-v00) ^ (v11-v00);                        
+                        (*d.normals)[i00] += normal2;                                                
+                    }
+                    else
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v10-v01);
+                        (*d.normals)[i00] += normal1;                        
+                        (*d.normals)[i01] += normal1;                                                                        
+
+                        osg::Vec3 normal2 = (v10-v01) ^ (v11-v01);
+                        (*d.normals)[i01] += normal2;                        
+                    }
+                }
+            }
+
+            //Recalculate the north side
+            if (n_neighbor.valid() && n_neighbor->getNumColumns() == d.numCols && n_neighbor->getNumRows() == d.numRows)
+            {                 
+                osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
+                boundaryVerts->reserve( 2 * d.numCols );
+
+                std::vector< float > boundaryElevations;
+                boundaryElevations.reserve( 2 * d.numCols );
+
+                //Compute the verts for the north side               
+                for (int j = 0; j <= 1; j++)
+                {
+                    for (int i = 0; i < (int)d.numCols; i++)                    
+                    {                           
+                        osg::Vec3d ndc( (double)(i)/(double)(d.numCols-1), (double)(d.numRows -1 + j)/(double)(d.numRows-1), 0.0);
+                        //osg::Vec3d ndc( (double)(i)/(double)(d.numCols-1), (double)(-static_cast<int>(j))/(double)(d.numRows-1), 0.0);                        
+                        
+                        //TODO:  Should probably use an interpolated method here
+                        float heightValue = n_neighbor->getHeight( i, j );
+                        ndc.z() = heightValue;                        
+
+                        osg::Vec3d model;
+                        d.model->_tileLocator->unitToModel( ndc, model );
+                        osg::Vec3d v = model - d.centerModel;
+                        boundaryVerts->push_back( v );
+                        boundaryElevations.push_back( heightValue );                        
+                    }
+                }   
+
+                //The boundary verts are now populated, so go through and triangulate them add add the normals to the existing normal array                
+                for (int i = 0; i < (int)d.numCols-1; i++)
+                {                    
+                    int i00;                    
+                    int j = d.numRows-1;
+                    if (swapOrientation)
+                    {         
+                        int i01 = j * d.numCols + i;
+                        i00 = i01+d.numCols;
+                    }
+                    else
+                    {
+                        i00 = j*d.numCols + i;                        
+                    }
+
+                    int i10 = i00+1;
+
+                    //remap indices to final vertex position
+                    i00 = d.indices[i00];
+                    i10 = d.indices[i10];
+
+                    int baseIndex = i;
+                    osg::Vec3f& v00 = (*boundaryVerts)[baseIndex    ];
+                    osg::Vec3f& v10 = (*boundaryVerts)[baseIndex + 1];
+                    osg::Vec3f& v01 = (*boundaryVerts)[baseIndex + d.numCols];
+                    osg::Vec3f& v11 = (*boundaryVerts)[baseIndex + d.numCols + 1];
+
+                    float e00 = boundaryElevations[baseIndex];
+                    float e10 = boundaryElevations[baseIndex + 1];
+                    float e01 = boundaryElevations[baseIndex + d.numCols];
+                    float e11 = boundaryElevations[baseIndex + d.numCols + 1];
+
+                   
+                    if (!optimizeTriangleOrientation || (e00-e11)<fabsf(e01-e10))
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v11-v01);                       
+                        (*d.normals)[i00] += normal1;                        
+                        (*d.normals)[i10] += normal1;
+
+                        osg::Vec3 normal2 = (v10-v00) ^ (v11-v00);                        
+                        (*d.normals)[i10] += normal2;                                                
+                    }
+                    else
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v10-v01);
+                        (*d.normals)[i00] += normal1;                                                
+
+                        osg::Vec3 normal2 = (v10-v01) ^ (v11-v01);
+                        (*d.normals)[i00] += normal2;                                                
+                        (*d.normals)[i10] += normal2;                        
+                    }
+                }
+            }
+
+            //Recalculate the south side
+            if (s_neighbor.valid() && s_neighbor->getNumColumns() == d.numCols && s_neighbor->getNumRows() == d.numRows)
+            {                
+                osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
+                boundaryVerts->reserve( 2 * d.numCols );
+
+                std::vector< float > boundaryElevations;
+                boundaryElevations.reserve( 2 * d.numCols );
+
+                //Compute the verts for the south side               
+                for (int j = (int)d.numRows-2; j <= (int)d.numRows-1; j++)
+                {
+                    for (int i = 0; i < (int)d.numCols; i++)                    
+                    {                           
+                        osg::Vec3d ndc( (double)(i)/(double)(d.numCols-1), (double)(j - static_cast<int>(d.numRows-1))/(double)(d.numRows-1), 0.0);                                                
+                        
+                        //TODO:  Should probably use an interpolated method here
+                        float heightValue = s_neighbor->getHeight( i, j );
+                        ndc.z() = heightValue;                        
+
+                        osg::Vec3d model;
+                        d.model->_tileLocator->unitToModel( ndc, model );
+                        osg::Vec3d v = model - d.centerModel;
+                        boundaryVerts->push_back( v );
+                        boundaryElevations.push_back( heightValue );                        
+                    }
+                }   
+
+                //The boundary verts are now populated, so go through and triangulate them add add the normals to the existing normal array                
+                for (int i = 0; i < (int)d.numCols-1; i++)
+                {                    
+                    int i00;                    
+                    int j = 0;
+
+
+                    if (swapOrientation)
+                    {                   
+                        int i01 = j*d.numCols + i;
+                        i00 = i01+d.numCols;                    
+                    }
+                    else
+                    {
+                        i00 = j*d.numCols + i;                        
+                    }                    
+
+                    int i10 = i00+1;
+
+                    //remap indices to final vertex position
+                    i00 = d.indices[i00];
+                    i10 = d.indices[i10];
+
+                    int baseIndex = i;
+                    osg::Vec3f& v00 = (*boundaryVerts)[baseIndex    ];
+                    osg::Vec3f& v10 = (*boundaryVerts)[baseIndex + 1];
+                    osg::Vec3f& v01 = (*boundaryVerts)[baseIndex + d.numCols];
+                    osg::Vec3f& v11 = (*boundaryVerts)[baseIndex + d.numCols + 1];
+
+                    float e00 = boundaryElevations[baseIndex];
+                    float e10 = boundaryElevations[baseIndex + 1];
+                    float e01 = boundaryElevations[baseIndex + d.numCols];
+                    float e11 = boundaryElevations[baseIndex + d.numCols + 1];
+
+                   
+                    if (!optimizeTriangleOrientation || (e00-e11)<fabsf(e01-e10))
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v11-v01);                       
+                        (*d.normals)[i00] += normal1;                                                
+
+                        osg::Vec3 normal2 = (v10-v00) ^ (v11-v00);                        
+                        (*d.normals)[i00] += normal2;                                                
+                        (*d.normals)[i10] += normal2;                                                
+                    }
+                    else
+                    {                            
+                        osg::Vec3 normal1 = (v00-v01) ^ (v10-v01);
+                        (*d.normals)[i00] += normal1;                                                
+                        (*d.normals)[i10] += normal1;                                                
+
+                        osg::Vec3 normal2 = (v10-v01) ^ (v11-v01);                        
+                        (*d.normals)[i10] += normal2;                        
+                    }
+                }
+            }            
+        }
+
+        if (recalcNormals)
         {
             for( osg::Vec3Array::iterator nitr = d.normals->begin(); nitr != d.normals->end(); ++nitr )
             {
                 nitr->normalize();
-            }
+            }       
         }
     }
 
@@ -1497,7 +1843,7 @@ TileModelCompiler::compile(const TileModel* model,
         createSkirtGeometry( d, _texCompositor.get(), *_options.heightFieldSkirtRatio() );
 
     // tesselate the surface verts into triangles.
-    tessellateSurfaceGeometry( d, _optimizeTriOrientation );
+    tessellateSurfaceGeometry( d, _optimizeTriOrientation, *_options.normalizeEdges() );
 
     // assign our texture coordinate arrays to the geometry. This must happen LAST
     // since we're sharing arrays across tiles. Here is why:
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelFactory b/src/osgEarthDrivers/engine_quadtree/TileModelFactory
index 39439e7..6dbe44c 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelFactory
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelFactory
@@ -25,39 +25,142 @@
 #include "QuadTreeTerrainEngineOptions"
 #include <osgEarth/Map>
 #include <osgEarth/ThreadingUtils>
+#include <osgEarth/Containers>
+#include <osgEarth/HeightFieldUtils>
+#include <osgEarth/MapFrame>
+#include <osgEarth/MapInfo>
 #include <osg/Group>
 
-using namespace osgEarth;
-
-/**
- * For a given TileKey, this class builds a a corresponding TileNode from
- * the map's data model.
- *
- * TODO: This should probably change to TileModelFactory or something since
- * the creation of the TileNode itself is trivial and can be done outside of
- * this class.
- */
-class TileModelFactory : public osg::Referenced
+namespace osgEarth_engine_quadtree
 {
-public:
-    TileModelFactory(
-        const Map*                                   map,
-        TileNodeRegistry*                            liveTiles,
-        const Drivers::QuadTreeTerrainEngineOptions& terrainOptions );
-
-    /** dtor */
-    virtual ~TileModelFactory() { }
-
-    void createTileModel(
-        const TileKey&           key,
-        osg::ref_ptr<TileModel>& out_model,
-        bool&                    out_hasRealData,
-        bool&                    out_hasLodBlendedLayers );
-
-private:
-    const Map*                                   _map;
-    osg::ref_ptr<TileNodeRegistry>               _liveTiles;
-    const Drivers::QuadTreeTerrainEngineOptions& _terrainOptions;
-};
+    using namespace osgEarth;
+
+    struct HFKey {
+        TileKey _key;
+        bool    _fallback;
+        bool    _convertToHAE;
+        ElevationSamplePolicy _samplePolicy;
+        bool operator < (const HFKey& rhs) const {
+            if ( _key < rhs._key ) return true;
+            if ( rhs._key < _key ) return false;
+            if ( _fallback != rhs._fallback ) return true;
+            if ( _convertToHAE != rhs._convertToHAE ) return true;
+            return _samplePolicy < rhs._samplePolicy;
+        }
+    };
+
+    struct HFValue {
+        osg::ref_ptr<osg::HeightField> _hf;
+        bool                           _isFallback;
+    };        
+
+    class HeightFieldCache : public osg::Referenced, public Revisioned
+    {
+    public:
+        HeightFieldCache():
+          _cache    ( true, 128 )
+        {
+
+        }
+
+        bool getOrCreateHeightField( 
+                const MapFrame&                       frame,  
+                const TileKey&                  key,
+                bool                            fallback,
+                osg::ref_ptr<osg::HeightField>& out_hf,            
+                bool*                           out_isFallback =0L,   
+                bool                            convertToHAE   =true,         
+                ElevationSamplePolicy           samplePolicy   =SAMPLE_FIRST_VALID,
+                ProgressCallback*               progress       =0L ) const
+        {                
+            // check the quick cache.
+            HFKey cachekey;
+            cachekey._key          = key;
+            cachekey._fallback     = fallback;
+            cachekey._convertToHAE = convertToHAE;
+            cachekey._samplePolicy = samplePolicy;
+            LRUCache<HFKey,HFValue>::Record rec = _cache.get( cachekey );        
+
+            bool hit = false;
+            if ( rec.valid() )
+            {
+                out_hf = rec.value()._hf.get();
+                if ( out_isFallback )
+                    *out_isFallback = rec.value()._isFallback;                        
+                return true;            
+            }                
+
+            bool isFallback;
+
+            bool ok = frame.getHeightField( key, fallback, out_hf, &isFallback, convertToHAE, samplePolicy, progress );
+
+            if ( ok )
+            {            
+                // Treat Plate Carre specially by scaling the height values. (There is no need
+                // to do this with an empty heightfield)
+                const MapInfo& mapInfo = frame.getMapInfo();
+                if ( mapInfo.isPlateCarre() )
+                {
+                    HeightFieldUtils::scaleHeightFieldToDegrees( out_hf.get() );
+                }
+
+                if ( out_isFallback )
+                    *out_isFallback = isFallback;
+
+                // cache me
+                HFValue cacheval;
+                cacheval._hf = out_hf.get();
+                cacheval._isFallback = isFallback;
+                _cache.insert( cachekey, cacheval );
+            }
+
+            return ok;
+        }
+
+        void clear()
+        {
+            _cache.clear();
+        }
+
+    private:
+        mutable LRUCache<HFKey,HFValue> _cache;
+    };
+
+    /**
+     * For a given TileKey, this class builds a a corresponding TileNode from
+     * the map's data model.
+     *
+     * TODO: This should probably change to TileModelFactory or something since
+     * the creation of the TileNode itself is trivial and can be done outside of
+     * this class.
+     */
+    class TileModelFactory : public osg::Referenced
+    {
+    public:
+        TileModelFactory(
+            const Map*                                   map,
+            TileNodeRegistry*                            liveTiles,
+            const Drivers::QuadTreeTerrainEngineOptions& terrainOptions );
+
+        HeightFieldCache* getHeightFieldCache() const;
+
+        /** dtor */
+        virtual ~TileModelFactory() { }
+
+        void createTileModel(
+            const TileKey&           key,
+            osg::ref_ptr<TileModel>& out_model,
+            bool&                    out_hasRealData,
+            bool&                    out_hasLodBlendedLayers );
+
+    private:        
+
+        const Map*                                   _map;
+        osg::ref_ptr<TileNodeRegistry>               _liveTiles;
+        const Drivers::QuadTreeTerrainEngineOptions& _terrainOptions;
+        osg::ref_ptr< HeightFieldCache > _hfCache;
+    };
+
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_FACTORY
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp b/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
index 4b013bd..ae2bdcf 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
@@ -17,9 +17,12 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include "TileModelFactory"
+#include <osgEarth/MapFrame>
+#include <osgEarth/MapInfo>
 #include <osgEarth/ImageUtils>
 #include <osgEarth/HeightFieldUtils>
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 using namespace OpenThreads;
@@ -145,17 +148,18 @@ namespace
 {
     struct BuildElevationData
     {
-        void init(const TileKey& key, const MapFrame& mapf, const QuadTreeTerrainEngineOptions& opt, TileModel* model) //sourceTileNodeBuilder::SourceRepo& repo)
+        void init(const TileKey& key, const MapFrame& mapf, const QuadTreeTerrainEngineOptions& opt, TileModel* model, HeightFieldCache* hfCache) //sourceTileNodeBuilder::SourceRepo& repo)
         {
             _key   = key;
             _mapf  = &mapf;
             _opt   = &opt;
             _model = model;
+            _hfCache = hfCache;
             //_repo = &repo;
         }
 
         void execute()
-        {
+        {            
             const MapInfo& mapInfo = _mapf->getMapInfo();
 
             // Request a heightfield from the map, falling back on lower resolution tiles
@@ -163,14 +167,9 @@ namespace
             osg::ref_ptr<osg::HeightField> hf;
             bool isFallback = false;
 
-            if ( _mapf->getHeightField( _key, true, hf, &isFallback ) )
-            {
-                // Treat Plate Carre specially by scaling the height values. (There is no need
-                // to do this with an empty heightfield)
-                if ( mapInfo.isPlateCarre() )
-                {
-                    HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() );
-                }
+            //if ( _mapf->getHeightField( _key, true, hf, &isFallback ) )
+            if (_hfCache->getOrCreateHeightField( *_mapf, _key, true, hf, &isFallback) )
+            {                
 
                 // Put it in the repo
                 osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf.get() );
@@ -179,6 +178,34 @@ namespace
                 hfLayer->setLocator( GeoLocator::createForKey( _key, mapInfo ) );
 
                 _model->_elevationData = TileModel::ElevationData(hfLayer, isFallback);
+                
+                if ( *_opt->normalizeEdges() )
+                {
+                    // next, query the neighboring tiles to get adjacency information.
+                    for( int x=-1; x<=1; x++ )
+                    {
+                        for( int y=-1; y<=1; y++ )
+                        {
+                            if ( x != 0 || y != 0 )
+                            {
+                                TileKey nk = _key.createNeighborKey(x, y);
+                                if ( nk.valid() )
+                                {
+                                    if (_hfCache->getOrCreateHeightField( *_mapf, nk, true, hf, &isFallback) )
+                                    //if ( _mapf->getHeightField(nk, true, hf, &isFallback) )
+                                    {               
+                                        if ( mapInfo.isPlateCarre() )
+                                        {
+                                            HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() );
+                                        }
+
+                                        _model->_elevationData.setNeighbor( x, y, hf.get() );
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
             }
         }
 
@@ -186,6 +213,7 @@ namespace
         const MapFrame*          _mapf;
         const QuadTreeTerrainEngineOptions* _opt;
         TileModel* _model;
+        osg::ref_ptr< HeightFieldCache> _hfCache;
     };
 }
 
@@ -198,7 +226,13 @@ _map           ( map ),
 _liveTiles     ( liveTiles ),
 _terrainOptions( terrainOptions )
 {
-    //nop
+    _hfCache = new HeightFieldCache();
+}
+
+HeightFieldCache*
+TileModelFactory::getHeightFieldCache() const
+{
+    return _hfCache;
 }
 
 
@@ -242,7 +276,7 @@ TileModelFactory::createTileModel(const TileKey&           key,
 
     // make an elevation layer.
     BuildElevationData build;
-    build.init( key, mapf, _terrainOptions, model.get() );
+    build.init( key, mapf, _terrainOptions, model.get(), _hfCache );
     build.execute();
 
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNode b/src/osgEarthDrivers/engine_quadtree/TileNode
index 3102c87..6557259 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNode
+++ b/src/osgEarthDrivers/engine_quadtree/TileNode
@@ -26,81 +26,84 @@
 #include <osg/Group>
 #include <vector>
 
-using namespace osgEarth;
+namespace osgEarth_engine_quadtree
+{
+    using namespace osgEarth;
 
-//------------------------------------------------------------------------
+    //------------------------------------------------------------------------
 
-/**
- * Node that represents a single quadtree terrain tile corresponding to 
- * unique TileKey.
- */
-class TileNode : public osg::Group
-{
-public:
     /**
-     * Constructs a new tile node
+     * Node that represents a single quadtree terrain tile corresponding to 
+     * unique TileKey.
      */
-    TileNode( const TileKey& key, GeoLocator* keyLocator );
+    class TileNode : public osg::Group
+    {
+    public:
+        /**
+         * Constructs a new tile node
+         */
+        TileNode( const TileKey& key, GeoLocator* keyLocator );
 
-    /**
-     * The tilekey associated with this tile
-     */
-    const TileKey& getKey() const { return _key; }
+        /**
+         * The tilekey associated with this tile
+         */
+        const TileKey& getKey() const { return _key; }
 
-    /**
-     * The data model used to create this node's geometry.
-     */
-    void setTileModel( TileModel* model );
-    TileModel* getTileModel() { return _model.get(); }
+        /**
+         * The data model used to create this node's geometry.
+         */
+        void setTileModel( TileModel* model );
+        TileModel* getTileModel() { return _model.get(); }
 
-    /**
-     * Compiles this node's tile model into geometry.
-     * @param[in ] compiler     Compiler that will compiler the model
-     * @param[in ] releaseModel Whether to deallocate the tile model after compilation
-     * @return True upon success
-     */
-    bool compile( TileModelCompiler* compiler, bool releaseModel =true );
+        /**
+         * Compiles this node's tile model into geometry.
+         * @param[in ] compiler     Compiler that will compiler the model
+         * @param[in ] releaseModel Whether to deallocate the tile model after compilation
+         * @return True upon success
+         */
+        bool compile( TileModelCompiler* compiler, bool releaseModel =true );
 
-    /**
-     * The locator for geometry in this tile
-     */
-    GeoLocator* getLocator() const { return _locator.get(); }
+        /**
+         * The locator for geometry in this tile
+         */
+        GeoLocator* getLocator() const { return _locator.get(); }
 
-    /**
-     * The public state set associated with the compiled tile model.
-     */
-    osg::StateSet* getPublicStateSet() const { return _publicStateSet; }
+        /**
+         * The public state set associated with the compiled tile model.
+         */
+        osg::StateSet* getPublicStateSet() const { return _publicStateSet; }
 
 
-public: // OVERRIDES
+    public: // OVERRIDES
 
-    virtual void traverse( class osg::NodeVisitor& nv );
+        virtual void traverse( class osg::NodeVisitor& nv );
 
-    //virtual osg::BoundingSphere computeBound() const;
+        //virtual osg::BoundingSphere computeBound() const;
 
-protected:
+    protected:
 
-    virtual ~TileNode();
+        virtual ~TileNode();
 
-    bool                      _cullTraversed;
-    TileKey                   _key;
-    osg::ref_ptr<GeoLocator>  _locator;
-    osg::ref_ptr<TileModel>   _model;
-    osg::StateSet*            _publicStateSet;
-};
+        bool                      _cullTraversed;
+        TileKey                   _key;
+        osg::ref_ptr<GeoLocator>  _locator;
+        osg::ref_ptr<TileModel>   _model;
+        osg::StateSet*            _publicStateSet;
+    };
 
 
-typedef std::vector< osg::ref_ptr<TileNode> > TileNodeVector;
+    typedef std::vector< osg::ref_ptr<TileNode> > TileNodeVector;
 
 
-/**
- * Marker class. 
- */
-class TileNodeGroup : public osg::Group
-{
-public:
-    TileNodeGroup() : osg::Group() { }
-};
+    /**
+     * Marker class. 
+     */
+    class TileNodeGroup : public osg::Group
+    {
+    public:
+        TileNodeGroup() : osg::Group() { }
+    };
 
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_TILE_NODE
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNode.cpp b/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
index 0d02019..b72752a 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
@@ -23,6 +23,7 @@
 #include <osg/NodeCallback>
 #include <osg/NodeVisitor>
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
index 1fa0ac2..cd64a29 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
+++ b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
@@ -24,59 +24,63 @@
 #include <osgEarth/ThreadingUtils>
 #include <map>
 
-using namespace osgEarth;
-
-/**
- * Holds a reference to each tile created by the driver.
- */
-class TileNodeRegistry : public osg::Referenced
+namespace osgEarth_engine_quadtree
 {
-public:
-    typedef std::map< TileKey, osg::ref_ptr<TileNode> > TileNodeMap;
+    using namespace osgEarth;
 
-    // Proprtype for a locked tileset operation (see run)
-    struct Operation {
-        virtual void operator()( TileNodeMap& tiles ) =0;
-    };
+    /**
+     * Holds a reference to each tile created by the driver.
+     */
+    class TileNodeRegistry : public osg::Referenced
+    {
+    public:
+        typedef std::map< TileKey, osg::ref_ptr<TileNode> > TileNodeMap;
 
-    struct ConstOperation {
-        virtual void operator()( const TileNodeMap& tiles ) const =0;
-    };
+        // Proprtype for a locked tileset operation (see run)
+        struct Operation {
+            virtual void operator()( TileNodeMap& tiles ) =0;
+        };
 
-public:
-    TileNodeRegistry( const std::string& name );
+        struct ConstOperation {
+            virtual void operator()( const TileNodeMap& tiles ) const =0;
+        };
 
-    virtual ~TileNodeRegistry() { }
+    public:
+        TileNodeRegistry( const std::string& name );
 
-    /** Adds a tile to the registry */
-    void add( TileNode* tile );
+        virtual ~TileNodeRegistry() { }
 
-    /** Adds several tiles to the registry */
-    void add( const TileNodeVector& tiles );
+        /** Adds a tile to the registry */
+        void add( TileNode* tile );
 
-    /** Moves a tile to the "removed" list */
-    void remove( TileNode* tile );
+        /** Adds several tiles to the registry */
+        void add( const TileNodeVector& tiles );
 
-    /** Finds a tile in the registry */
-    bool get( const TileKey& key, osg::ref_ptr<TileNode>& out_tile );
+        /** Moves a tile to the "removed" list */
+        void remove( TileNode* tile );
 
-    /** Finds a tile in the registry and then removes it. */
-    bool take( const TileKey& key, osg::ref_ptr<TileNode>& out_tile );
+        /** Finds a tile in the registry */
+        bool get( const TileKey& key, osg::ref_ptr<TileNode>& out_tile );
 
-    /** Whether there are tiles in this registry (snapshot in time) */
-    bool empty() const;
+        /** Finds a tile in the registry and then removes it. */
+        bool take( const TileKey& key, osg::ref_ptr<TileNode>& out_tile );
 
-    /** Runs an operation against the exclusively locked tile set. */
-    void run( Operation& op );
-    
-    /** Runs an operation against the read-locked tile set. */
-    void run( const ConstOperation& op ) const;
+        /** Whether there are tiles in this registry (snapshot in time) */
+        bool empty() const;
 
-protected:
+        /** Runs an operation against the exclusively locked tile set. */
+        void run( Operation& op );
+        
+        /** Runs an operation against the read-locked tile set. */
+        void run( const ConstOperation& op ) const;
+
+    protected:
+
+        std::string                       _name;
+        TileNodeMap                       _tiles;
+        mutable Threading::ReadWriteMutex _tilesMutex;
+    };
 
-    std::string                       _name;
-    TileNodeMap                       _tiles;
-    mutable Threading::ReadWriteMutex _tilesMutex;
-};
+} // namespace osgEarth_engine_quadtree
 
 #endif // OSGEARTH_ENGINE_QUADTREE_TILE_NODE_REGISTRY
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
index 051bdba..f8ddf42 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
@@ -18,6 +18,7 @@
 */
 #include "TileNodeRegistry"
 
+using namespace osgEarth_engine_quadtree;
 using namespace osgEarth;
 
 #define LC "[TileNodeRegistry] "
diff --git a/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp b/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp
index 9faddbf..be2f509 100644
--- a/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp
+++ b/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp
@@ -154,7 +154,7 @@ public:
             // attempt to open the dataset:
             int openMode = _options.openWrite().isSet() && _options.openWrite().value() ? 1 : 0;
 
-	        _dsHandle = OGROpenShared( _source.c_str(), openMode, &_ogrDriverHandle );
+	        _dsHandle = OGROpen( _source.c_str(), openMode, &_ogrDriverHandle );
 	        if ( _dsHandle )
 	        {
                 if (openMode == 1) _writable = true;
diff --git a/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp b/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp
index fd28c20..a2955d6 100644
--- a/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp
+++ b/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp
@@ -1080,7 +1080,7 @@ public:
     /**
     * Finds a raster band based on color interpretation 
     */
-    static GDALRasterBand* findBand(GDALDataset *ds, GDALColorInterp colorInterp)
+    static GDALRasterBand* findBandByColorInterp(GDALDataset *ds, GDALColorInterp colorInterp)
     {
         GDAL_SCOPED_LOCK;
 
@@ -1091,6 +1091,17 @@ public:
         return 0;
     }
 
+    static GDALRasterBand* findBandByDataType(GDALDataset *ds, GDALDataType dataType)
+    {
+        GDAL_SCOPED_LOCK;
+
+        for (int i = 1; i <= ds->GetRasterCount(); ++i)
+        {
+            if (ds->GetRasterBand(i)->GetRasterDataType() == dataType) return ds->GetRasterBand(i);
+        }
+        return 0;
+    }
+
     static void getPalleteIndexColor(GDALRasterBand* band, int index, osg::Vec4ub& color)
     {
         const GDALColorEntry *colorEntry = band->GetColorTable()->GetColorEntry( index );
@@ -1253,14 +1264,14 @@ public:
 
 
 
-            GDALRasterBand* bandRed = findBand(_warpedDS, GCI_RedBand);
-            GDALRasterBand* bandGreen = findBand(_warpedDS, GCI_GreenBand);
-            GDALRasterBand* bandBlue = findBand(_warpedDS, GCI_BlueBand);
-            GDALRasterBand* bandAlpha = findBand(_warpedDS, GCI_AlphaBand);
+            GDALRasterBand* bandRed = findBandByColorInterp(_warpedDS, GCI_RedBand);
+            GDALRasterBand* bandGreen = findBandByColorInterp(_warpedDS, GCI_GreenBand);
+            GDALRasterBand* bandBlue = findBandByColorInterp(_warpedDS, GCI_BlueBand);
+            GDALRasterBand* bandAlpha = findBandByColorInterp(_warpedDS, GCI_AlphaBand);
 
-            GDALRasterBand* bandGray = findBand(_warpedDS, GCI_GrayIndex);
+            GDALRasterBand* bandGray = findBandByColorInterp(_warpedDS, GCI_GrayIndex);
 
-            GDALRasterBand* bandPalette = findBand(_warpedDS, GCI_PaletteIndex);
+            GDALRasterBand* bandPalette = findBandByColorInterp(_warpedDS, GCI_PaletteIndex);
 
             if (!bandRed && !bandGreen && !bandBlue && !bandAlpha && !bandGray && !bandPalette)
             {
@@ -1700,8 +1711,13 @@ public:
             double xmin, ymin, xmax, ymax;
             key.getExtent().getBounds(xmin, ymin, xmax, ymax);
 
-            //Just read from the first band
-            GDALRasterBand* band = _warpedDS->GetRasterBand(1);
+            // Try to find a FLOAT band
+            GDALRasterBand* band = findBandByDataType(_warpedDS, GDT_Float32);
+            if (band == NULL)
+            {
+                // Just get first band
+                band = _warpedDS->GetRasterBand(1);
+            }
 
             double dx = (xmax - xmin) / (tileSize-1);
             double dy = (ymax - ymin) / (tileSize-1);
diff --git a/src/osgEarthDrivers/kml/KML b/src/osgEarthDrivers/kml/KML
index 81309c6..00f9089 100644
--- a/src/osgEarthDrivers/kml/KML
+++ b/src/osgEarthDrivers/kml/KML
@@ -26,8 +26,8 @@
 #include <osgDB/FileNameUtils>
 #include "KMLOptions"
 
-namespace osgEarth { namespace Drivers {
-
+namespace osgEarth { namespace Drivers
+{
     using namespace osgEarth;
 
     class KML // header-only (no export)
diff --git a/src/osgEarthDrivers/kml/KMLReader b/src/osgEarthDrivers/kml/KMLReader
index d7e60a2..2c441da 100644
--- a/src/osgEarthDrivers/kml/KMLReader
+++ b/src/osgEarthDrivers/kml/KMLReader
@@ -26,28 +26,32 @@
 #include <iostream>
 #include "KMLOptions"
 
-using namespace osgEarth;
-using namespace osgEarth::Drivers;
-
-class KMLReader
+namespace osgEarth_kml
 {
-public:
-    /** Initialized a KML reader that will work with the provided map node */
-    KMLReader( MapNode* mapNode, const KMLOptions* options );
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
+
+    class KMLReader
+    {
+    public:
+        /** Initialized a KML reader that will work with the provided map node */
+        KMLReader( MapNode* mapNode, const KMLOptions* options );
+
+        /** dtor */
+        virtual ~KMLReader() { }
 
-    /** dtor */
-    virtual ~KMLReader() { }
+        /** Reads KML from a stream and returns a node */
+        osg::Node* read( std::istream& in, const osgDB::Options* dbOptions ) ;
 
-    /** Reads KML from a stream and returns a node */
-    osg::Node* read( std::istream& in, const osgDB::Options* dbOptions ) ;
+        /** Reads KML from a Config object */
+        osg::Node* read( const Config& conf, const osgDB::Options* dbOptions );
 
-    /** Reads KML from a Config object */
-    osg::Node* read( const Config& conf, const osgDB::Options* dbOptions );
+    private:
+        MapNode*          _mapNode;
+        const KMLOptions* _options;
+    };
 
-private:
-    MapNode*          _mapNode;
-    const KMLOptions* _options;
-};
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_READER
 
diff --git a/src/osgEarthDrivers/kml/KMLReader.cpp b/src/osgEarthDrivers/kml/KMLReader.cpp
index 50bd50e..c857127 100644
--- a/src/osgEarthDrivers/kml/KMLReader.cpp
+++ b/src/osgEarthDrivers/kml/KMLReader.cpp
@@ -22,11 +22,12 @@
 #include <osgEarth/Capabilities>
 #include <osgEarth/XmlUtils>
 #include <osgEarth/ShaderGenerator>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarthAnnotation/Decluttering>
 #include <stack>
 #include <iterator>
 
+using namespace osgEarth_kml;
 using namespace osgEarth;
 
 KMLReader::KMLReader( MapNode* mapNode, const KMLOptions* options ) :
@@ -108,15 +109,5 @@ KMLReader::read( const Config& conf, const osgDB::Options* dbOptions )
     CacheStats stats = cacheUsed->getStats();
     OE_INFO << LC << "URI Cache: " << stats._queries << " reads, " << (stats._hitRatio*100.0) << "% hits" << std::endl;
 
-    if ( Registry::capabilities().supportsGLSL() )
-    {
-        ShaderGenerator gen;
-        root->accept( gen );
-
-        VirtualProgram* vp = new VirtualProgram();
-        vp->installDefaultColoringAndLightingShaders();
-        root->getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
-    }
-
     return root;
 }
diff --git a/src/osgEarthDrivers/kml/KML_Common b/src/osgEarthDrivers/kml/KML_Common
index 5ef4884..32b3be0 100644
--- a/src/osgEarthDrivers/kml/KML_Common
+++ b/src/osgEarthDrivers/kml/KML_Common
@@ -32,10 +32,6 @@
 
 #define LC "[KML] "
 
-using namespace osgEarth;
-using namespace osgEarth::Drivers;
-using namespace osgEarth::Symbology;
-
 #define for_one( NAME, FUNC, CONF, CX ) \
 { \
     Config c = conf.child( toLower( #NAME ) ); \
@@ -63,43 +59,50 @@ using namespace osgEarth::Symbology;
     for_many( NetworkLink,   FUNC, CONF, CX ); \
     for_many( Placemark,     FUNC, CONF, CX );
 
-struct KMLContext
+namespace osgEarth_kml
 {
-    MapNode*                              _mapNode;         // reference map node
-    const KMLOptions*                     _options;         // user options
-    osg::ref_ptr<StyleSheet>              _sheet;           // entire style sheet
-    Style                                 _activeStyle;     // currently active style
-    std::stack<osg::ref_ptr<osg::Group> > _groupStack;      // resulting scene graph
-    osg::ref_ptr<const SpatialReference>  _srs;             // map's spatial reference
-    osg::ref_ptr<const osgDB::Options>    _dbOptions;       // I/O options (caching, etc)
-};
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
+    using namespace osgEarth::Symbology;
 
-struct KMLUtils
-{
-    // parse KML's many variants on a URL link.
-    static std::string parseLink( const Config& conf )
+    struct KMLContext
     {
-        Config link = conf.child("link");
-        if ( !link.empty() )
-        {
-            if ( link.hasValue("href") )
-                return link.value("href");
-            else if ( link.hasValue("url") )
-                return link.value("url");
-            else
-                return link.value();
-        }
-        else
+        MapNode*                              _mapNode;         // reference map node
+        const KMLOptions*                     _options;         // user options
+        osg::ref_ptr<StyleSheet>              _sheet;           // entire style sheet
+        Style                                 _activeStyle;     // currently active style
+        std::stack<osg::ref_ptr<osg::Group> > _groupStack;      // resulting scene graph
+        osg::ref_ptr<const SpatialReference>  _srs;             // map's spatial reference
+        osg::ref_ptr<const osgDB::Options>    _dbOptions;       // I/O options (caching, etc)
+    };
+
+    struct KMLUtils
+    {
+        // parse KML's many variants on a URL link.
+        static std::string parseLink( const Config& conf )
         {
-            link = conf.child("url");
-            if ( link.hasValue("href") )
-                return link.value("href");
+            Config link = conf.child("link");
+            if ( !link.empty() )
+            {
+                if ( link.hasValue("href") )
+                    return link.value("href");
+                else if ( link.hasValue("url") )
+                    return link.value("url");
+                else
+                    return link.value();
+            }
             else
-                return link.value();
+            {
+                link = conf.child("url");
+                if ( link.hasValue("href") )
+                    return link.value("href");
+                else
+                    return link.value();
+            }
         }
-    }
-};
+    };
 
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_READER
 
diff --git a/src/osgEarthDrivers/kml/KML_Container b/src/osgEarthDrivers/kml/KML_Container
index 3354e19..55be876 100644
--- a/src/osgEarthDrivers/kml/KML_Container
+++ b/src/osgEarthDrivers/kml/KML_Container
@@ -22,26 +22,30 @@
 #include "KML_Common"
 #include "KML_Feature"
 
-using namespace osgEarth;
-
-struct KML_Container : public KML_Feature
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx )
-    {
-        KML_Feature::scan(conf, cx);
-    }
+    using namespace osgEarth;
 
-    virtual void scan2( const Config& conf, KMLContext& cx )
+    struct KML_Container : public KML_Feature
     {
-        KML_Feature::scan2(conf, cx);
-    }
+        virtual void scan( const Config& conf, KMLContext& cx )
+        {
+            KML_Feature::scan(conf, cx);
+        }
 
-    virtual void build( const Config& conf, KMLContext& cx, osg::Node* working )
-    {
-        // assumes the top of the group stack has a new and valid Node.
-        // don't call this is there was an error in the subclass build() method
-        KML_Feature::build(conf, cx, working);
-    }
-};
+        virtual void scan2( const Config& conf, KMLContext& cx )
+        {
+            KML_Feature::scan2(conf, cx);
+        }
+
+        virtual void build( const Config& conf, KMLContext& cx, osg::Node* working )
+        {
+            // assumes the top of the group stack has a new and valid Node.
+            // don't call this is there was an error in the subclass build() method
+            KML_Feature::build(conf, cx, working);
+        }
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_CONTAINER
diff --git a/src/osgEarthDrivers/kml/KML_Document b/src/osgEarthDrivers/kml/KML_Document
index b3e7a1b..e269453 100644
--- a/src/osgEarthDrivers/kml/KML_Document
+++ b/src/osgEarthDrivers/kml/KML_Document
@@ -22,16 +22,20 @@
 #include "KML_Common"
 #include "KML_Container"
 
-using namespace osgEarth;
-
-struct KML_Document : public KML_Container
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
+    using namespace osgEarth;
+
+    struct KML_Document : public KML_Container
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+
+        virtual void scan2( const Config& conf, KMLContext& cx );
 
-    virtual void scan2( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
+    };
 
-    virtual void build( const Config& conf, KMLContext& cx );
-};
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_DOCUMENT
 
diff --git a/src/osgEarthDrivers/kml/KML_Document.cpp b/src/osgEarthDrivers/kml/KML_Document.cpp
index 169b653..4386dec 100644
--- a/src/osgEarthDrivers/kml/KML_Document.cpp
+++ b/src/osgEarthDrivers/kml/KML_Document.cpp
@@ -25,6 +25,8 @@
 #include "KML_NetworkLink"
 #include "KML_Placemark"
 
+using namespace osgEarth_kml;
+
 void
 KML_Document::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Feature b/src/osgEarthDrivers/kml/KML_Feature
index a3f7eca..964206b 100644
--- a/src/osgEarthDrivers/kml/KML_Feature
+++ b/src/osgEarthDrivers/kml/KML_Feature
@@ -21,15 +21,19 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_Feature : public KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
+    using namespace osgEarth;
+
+    struct KML_Feature : public KML_Object
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+
+        virtual void scan2( const Config& conf, KMLContext& cx );
 
-    virtual void scan2( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx, osg::Node* working );
+    };
 
-    virtual void build( const Config& conf, KMLContext& cx, osg::Node* working );
-};
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_FEATURE
diff --git a/src/osgEarthDrivers/kml/KML_Feature.cpp b/src/osgEarthDrivers/kml/KML_Feature.cpp
index a389de0..1c98a3b 100644
--- a/src/osgEarthDrivers/kml/KML_Feature.cpp
+++ b/src/osgEarthDrivers/kml/KML_Feature.cpp
@@ -19,8 +19,11 @@
 #include "KML_Feature"
 #include "KML_Style"
 #include "KML_StyleMap"
+#include <osg/UserDataContainer>
+#include <osg/ValueObject>
 #include <osgEarth/Viewpoint>
 
+using namespace osgEarth_kml;
 using namespace osgEarth;
 
 void
@@ -49,7 +52,7 @@ KML_Feature::build( const Config& conf, KMLContext& cx, osg::Node* working )
     {
         // parse the visibility to show/hide the item by default:
         if ( conf.hasValue("visibility") )
-            working->setNodeMask( conf.value<bool>("visibility",true) == true ? ~0 : 0 );
+            working->setNodeMask( conf.value<int>("visibility", 1) == 1 ? ~0 : 0 );
 
         // parse a "LookAt" element (stores a viewpoint)
         AnnotationData* anno = getOrCreateAnnotationData(working);
@@ -70,5 +73,15 @@ KML_Feature::build( const Config& conf, KMLContext& cx, osg::Node* working )
 
             anno->setViewpoint( vp );
         }
+
+        const Config& extdata = conf.child("extendeddata");
+        if ( !extdata.empty() )
+        {
+            ConfigSet innerConfs = extdata.children("data");
+            for( ConfigSet::const_iterator i = innerConfs.begin(); i != innerConfs.end(); ++i )
+            {
+                working->setUserValue(i->value("name"), i->value("value"));
+            }
+        }
     }
 }
diff --git a/src/osgEarthDrivers/kml/KML_Folder b/src/osgEarthDrivers/kml/KML_Folder
index 82d2cd9..6463a2e 100644
--- a/src/osgEarthDrivers/kml/KML_Folder
+++ b/src/osgEarthDrivers/kml/KML_Folder
@@ -22,15 +22,19 @@
 #include "KML_Common"
 #include "KML_Container"
 
-using namespace osgEarth;
-
-struct KML_Folder : public KML_Container
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
+    using namespace osgEarth;
+
+    struct KML_Folder : public KML_Container
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+
+        virtual void scan2( const Config& conf, KMLContext& cx );
 
-    virtual void scan2( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
+    };
 
-    virtual void build( const Config& conf, KMLContext& cx );
-};
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_FOLDER
diff --git a/src/osgEarthDrivers/kml/KML_Folder.cpp b/src/osgEarthDrivers/kml/KML_Folder.cpp
index 87d8317..b5cde3b 100644
--- a/src/osgEarthDrivers/kml/KML_Folder.cpp
+++ b/src/osgEarthDrivers/kml/KML_Folder.cpp
@@ -25,6 +25,8 @@
 #include "KML_NetworkLink"
 #include "KML_Placemark"
 
+using namespace osgEarth_kml;
+
 void
 KML_Folder::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Geometry b/src/osgEarthDrivers/kml/KML_Geometry
index a10b53f..bb4ad28 100644
--- a/src/osgEarthDrivers/kml/KML_Geometry
+++ b/src/osgEarthDrivers/kml/KML_Geometry
@@ -22,19 +22,23 @@
 #include "KML_Object"
 #include <osgEarthSymbology/Geometry>
 
-using namespace osgEarth;
-using namespace osgEarth::Symbology;
-
-struct KML_Geometry : public KML_Object
+namespace osgEarth_kml
 {
-    KML_Geometry() : _extrude(false), _tessellate(false) { }
-    virtual void parseCoords( const Config& conf, KMLContext& cx );
-    virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style );
-    virtual void build( const Config& confParent, KMLContext& cx, Style& style );
-    void buildChild( const Config& conf, KMLContext& cx, Style& style );
-    osg::ref_ptr<Geometry> _geom;
-    bool _extrude, _tessellate;
-private:    
-};
+    using namespace osgEarth;
+    using namespace osgEarth::Symbology;
+
+    struct KML_Geometry : public KML_Object
+    {
+        KML_Geometry() : _extrude(false), _tessellate(false) { }
+        virtual void parseCoords( const Config& conf, KMLContext& cx );
+        virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style );
+        virtual void build( const Config& confParent, KMLContext& cx, Style& style );
+        void buildChild( const Config& conf, KMLContext& cx, Style& style );
+        osg::ref_ptr<Geometry> _geom;
+        bool _extrude, _tessellate;
+    private:    
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_GEOMETRY
diff --git a/src/osgEarthDrivers/kml/KML_Geometry.cpp b/src/osgEarthDrivers/kml/KML_Geometry.cpp
index 848cab5..6432be6 100644
--- a/src/osgEarthDrivers/kml/KML_Geometry.cpp
+++ b/src/osgEarthDrivers/kml/KML_Geometry.cpp
@@ -25,6 +25,8 @@
 #include "KML_Model"
 #include <osgEarth/StringUtils>
 
+using namespace osgEarth_kml;
+
 void 
 KML_Geometry::build( const Config& parentConf, KMLContext& cx, Style& style)
 {
diff --git a/src/osgEarthDrivers/kml/KML_GroundOverlay b/src/osgEarthDrivers/kml/KML_GroundOverlay
index 9e34821..6439f08 100644
--- a/src/osgEarthDrivers/kml/KML_GroundOverlay
+++ b/src/osgEarthDrivers/kml/KML_GroundOverlay
@@ -22,12 +22,16 @@
 #include "KML_Common"
 #include "KML_Overlay"
 
-using namespace osgEarth;
-
-struct KML_GroundOverlay : public KML_Overlay
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-    virtual void build( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_GroundOverlay : public KML_Overlay
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_GROUNDOVERLAY
diff --git a/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp b/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp
index d0f3a5c..dfe8b5d 100644
--- a/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp
@@ -20,6 +20,7 @@
 #include "KML_Geometry"
 #include <osgEarthAnnotation/ImageOverlay>
 
+using namespace osgEarth_kml;
 using namespace osgEarth::Annotation;
 
 void
diff --git a/src/osgEarthDrivers/kml/KML_IconStyle b/src/osgEarthDrivers/kml/KML_IconStyle
index e6e40e0..30dea09 100644
--- a/src/osgEarthDrivers/kml/KML_IconStyle
+++ b/src/osgEarthDrivers/kml/KML_IconStyle
@@ -21,11 +21,15 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_IconStyle : public KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, Style& style, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_IconStyle : public KML_Object
+    {
+        virtual void scan( const Config& conf, Style& style, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_ICONSTYLE
diff --git a/src/osgEarthDrivers/kml/KML_IconStyle.cpp b/src/osgEarthDrivers/kml/KML_IconStyle.cpp
index 3669b94..8b5ad71 100644
--- a/src/osgEarthDrivers/kml/KML_IconStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_IconStyle.cpp
@@ -19,6 +19,8 @@
 #include "KML_IconStyle"
 #include <osgEarthSymbology/IconSymbol>
 
+using namespace osgEarth_kml;
+
 void
 KML_IconStyle::scan( const Config& conf, Style& style, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_LabelStyle b/src/osgEarthDrivers/kml/KML_LabelStyle
index 7fba448..8f619c6 100644
--- a/src/osgEarthDrivers/kml/KML_LabelStyle
+++ b/src/osgEarthDrivers/kml/KML_LabelStyle
@@ -21,11 +21,15 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_LabelStyle : public KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, Style& style, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_LabelStyle : public KML_Object
+    {
+        virtual void scan( const Config& conf, Style& style, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_LABELSTYLE
diff --git a/src/osgEarthDrivers/kml/KML_LabelStyle.cpp b/src/osgEarthDrivers/kml/KML_LabelStyle.cpp
index d0d831f..807492d 100644
--- a/src/osgEarthDrivers/kml/KML_LabelStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_LabelStyle.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_LabelStyle"
 
+using namespace osgEarth_kml;
+
 void 
 KML_LabelStyle::scan( const Config& conf, Style& style, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_LineString b/src/osgEarthDrivers/kml/KML_LineString
index aa91f49..ca2bdc3 100644
--- a/src/osgEarthDrivers/kml/KML_LineString
+++ b/src/osgEarthDrivers/kml/KML_LineString
@@ -21,12 +21,16 @@
 
 #include "KML_Geometry"
 
-using namespace osgEarth;
-
-struct KML_LineString : public KML_Geometry
+namespace osgEarth_kml
 {
-    virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style );
-    virtual void parseCoords( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_LineString : public KML_Geometry
+    {
+        virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style );
+        virtual void parseCoords( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_LINESTRING
diff --git a/src/osgEarthDrivers/kml/KML_LineString.cpp b/src/osgEarthDrivers/kml/KML_LineString.cpp
index ea38ce1..5e37a34 100644
--- a/src/osgEarthDrivers/kml/KML_LineString.cpp
+++ b/src/osgEarthDrivers/kml/KML_LineString.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_LineString"
 
+using namespace osgEarth_kml;
+
 void
 KML_LineString::parseStyle( const Config& conf, KMLContext& cs, Style& style )
 {
diff --git a/src/osgEarthDrivers/kml/KML_LineStyle b/src/osgEarthDrivers/kml/KML_LineStyle
index 473ae4d..35a6f5b 100644
--- a/src/osgEarthDrivers/kml/KML_LineStyle
+++ b/src/osgEarthDrivers/kml/KML_LineStyle
@@ -21,11 +21,15 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_LineStyle : public KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, Style& style, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_LineStyle : public KML_Object
+    {
+        virtual void scan( const Config& conf, Style& style, KMLContext& cx );
+    };
+
+} //namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_LINESTYLE
diff --git a/src/osgEarthDrivers/kml/KML_LineStyle.cpp b/src/osgEarthDrivers/kml/KML_LineStyle.cpp
index 76150e3..743d9c8 100644
--- a/src/osgEarthDrivers/kml/KML_LineStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_LineStyle.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_LineStyle"
 
+using namespace osgEarth_kml;
+
 void 
 KML_LineStyle::scan( const Config& conf, Style& style, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_LinearRing b/src/osgEarthDrivers/kml/KML_LinearRing
index 56df748..3344de7 100644
--- a/src/osgEarthDrivers/kml/KML_LinearRing
+++ b/src/osgEarthDrivers/kml/KML_LinearRing
@@ -21,12 +21,16 @@
 
 #include "KML_Geometry"
 
-using namespace osgEarth;
-
-struct KML_LinearRing : public KML_Geometry
+namespace osgEarth_kml
 {
-    virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style );
-    virtual void parseCoords( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_LinearRing : public KML_Geometry
+    {
+        virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style );
+        virtual void parseCoords( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_LINEARRING
diff --git a/src/osgEarthDrivers/kml/KML_LinearRing.cpp b/src/osgEarthDrivers/kml/KML_LinearRing.cpp
index c073125..fc34a27 100644
--- a/src/osgEarthDrivers/kml/KML_LinearRing.cpp
+++ b/src/osgEarthDrivers/kml/KML_LinearRing.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_LinearRing"
 
+using namespace osgEarth_kml;
+
 void
 KML_LinearRing::parseStyle( const Config& conf, KMLContext& cs, Style& style )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Model b/src/osgEarthDrivers/kml/KML_Model
index 205ce55..39156c1 100644
--- a/src/osgEarthDrivers/kml/KML_Model
+++ b/src/osgEarthDrivers/kml/KML_Model
@@ -21,12 +21,16 @@
 
 #include "KML_Geometry"
 
-using namespace osgEarth;
-
-struct KML_Model : public KML_Geometry
+namespace osgEarth_kml
 {
-    void parseCoords( const Config& conf, KMLContext& cx );
-    void parseStyle(const Config& conf, KMLContext& cx, Style& style);
-};
+    using namespace osgEarth;
+
+    struct KML_Model : public KML_Geometry
+    {
+        void parseCoords( const Config& conf, KMLContext& cx );
+        void parseStyle(const Config& conf, KMLContext& cx, Style& style);
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_MODEL
diff --git a/src/osgEarthDrivers/kml/KML_Model.cpp b/src/osgEarthDrivers/kml/KML_Model.cpp
index 798f31d..9638fab 100644
--- a/src/osgEarthDrivers/kml/KML_Model.cpp
+++ b/src/osgEarthDrivers/kml/KML_Model.cpp
@@ -20,6 +20,7 @@
 
 #include <osgEarthSymbology/ModelSymbol>
 
+using namespace osgEarth_kml;
 using namespace osgEarth::Symbology;
 
 void
diff --git a/src/osgEarthDrivers/kml/KML_MultiGeometry b/src/osgEarthDrivers/kml/KML_MultiGeometry
index 064701b..6bda743 100644
--- a/src/osgEarthDrivers/kml/KML_MultiGeometry
+++ b/src/osgEarthDrivers/kml/KML_MultiGeometry
@@ -21,11 +21,15 @@
 
 #include "KML_Geometry"
 
-using namespace osgEarth;
-
-struct KML_MultiGeometry : public KML_Geometry
+namespace osgEarth_kml
 {
-    virtual void parseCoords( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_MultiGeometry : public KML_Geometry
+    {
+        virtual void parseCoords( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_MULTIGEOMETRY
diff --git a/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp b/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp
index 8a2c319..98bc3de 100644
--- a/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp
+++ b/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_MultiGeometry"
 
+using namespace osgEarth_kml;
+
 void
 KML_MultiGeometry::parseCoords( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLink b/src/osgEarthDrivers/kml/KML_NetworkLink
index 1f70569..aa6952b 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLink
+++ b/src/osgEarthDrivers/kml/KML_NetworkLink
@@ -21,6 +21,7 @@
 
 #include "KML_Object"
 
+using namespace osgEarth_kml;
 using namespace osgEarth;
 
 struct KML_NetworkLink : public KML_Object
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLink.cpp b/src/osgEarthDrivers/kml/KML_NetworkLink.cpp
index 4de2386..b456e0a 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLink.cpp
+++ b/src/osgEarthDrivers/kml/KML_NetworkLink.cpp
@@ -26,6 +26,8 @@
 #undef  LC
 #define LC "[KML_NetworkLink] "
 
+using namespace osgEarth_kml;
+
 void
 KML_NetworkLink::build( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLinkControl b/src/osgEarthDrivers/kml/KML_NetworkLinkControl
index eb76e33..e906595 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLinkControl
+++ b/src/osgEarthDrivers/kml/KML_NetworkLinkControl
@@ -21,12 +21,16 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_NetworkLinkControl : public KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-    virtual void build( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_NetworkLinkControl : public KML_Object
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_NETWORKLINKCONTROL
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp b/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp
index e81783d..b27bdca 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp
+++ b/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_NetworkLinkControl"
 
+using namespace osgEarth_kml;
+
 void
 KML_NetworkLinkControl::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Object b/src/osgEarthDrivers/kml/KML_Object
index e195735..63c52ef 100644
--- a/src/osgEarthDrivers/kml/KML_Object
+++ b/src/osgEarthDrivers/kml/KML_Object
@@ -22,22 +22,26 @@
 #include "KML_Common"
 #include <osgEarthAnnotation/AnnotationData>
 
-using namespace osgEarth;
-using namespace osgEarth::Annotation;
-
-struct KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx ) { }
-    
-    virtual void scan2( const Config& conf, KMLContext& cx ) { }
+    using namespace osgEarth;
+    using namespace osgEarth::Annotation;
+
+    struct KML_Object
+    {
+        virtual void scan( const Config& conf, KMLContext& cx ) { }
+        
+        virtual void scan2( const Config& conf, KMLContext& cx ) { }
+
+        virtual void build( const Config& conf, KMLContext& cx, osg::Node* working );
 
-    virtual void build( const Config& conf, KMLContext& cx, osg::Node* working );
+        virtual ~KML_Object() { }
 
-    virtual ~KML_Object() { }
+    protected:
 
-protected:
+        AnnotationData* getOrCreateAnnotationData( osg::Node* node );
+    };
 
-    AnnotationData* getOrCreateAnnotationData( osg::Node* node );
-};
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_OBJECT
diff --git a/src/osgEarthDrivers/kml/KML_Object.cpp b/src/osgEarthDrivers/kml/KML_Object.cpp
index a31c114..2b97a4c 100644
--- a/src/osgEarthDrivers/kml/KML_Object.cpp
+++ b/src/osgEarthDrivers/kml/KML_Object.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_Object"
 
+using namespace osgEarth_kml;
+
 void
 KML_Object::build( const Config& conf, KMLContext& cx, osg::Node* working )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Overlay b/src/osgEarthDrivers/kml/KML_Overlay
index 8674948..77f943c 100644
--- a/src/osgEarthDrivers/kml/KML_Overlay
+++ b/src/osgEarthDrivers/kml/KML_Overlay
@@ -22,12 +22,16 @@
 #include "KML_Common"
 #include "KML_Feature"
 
-using namespace osgEarth;
-
-struct KML_Overlay : public KML_Feature
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-    virtual void build( const Config& conf, KMLContext& cx, osg::Node* working );
-};
+    using namespace osgEarth;
+
+    struct KML_Overlay : public KML_Feature
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx, osg::Node* working );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_OVERLAY
diff --git a/src/osgEarthDrivers/kml/KML_Overlay.cpp b/src/osgEarthDrivers/kml/KML_Overlay.cpp
index cf1b13e..b1beb5d 100644
--- a/src/osgEarthDrivers/kml/KML_Overlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_Overlay.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_Overlay"
 
+using namespace osgEarth_kml;
+
 void
 KML_Overlay::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_PhotoOverlay b/src/osgEarthDrivers/kml/KML_PhotoOverlay
index e77bef3..a35cd32 100644
--- a/src/osgEarthDrivers/kml/KML_PhotoOverlay
+++ b/src/osgEarthDrivers/kml/KML_PhotoOverlay
@@ -22,12 +22,16 @@
 #include "KML_Common"
 #include "KML_Overlay"
 
-using namespace osgEarth;
-
-struct KML_PhotoOverlay : public KML_Overlay
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-    virtual void build( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_PhotoOverlay : public KML_Overlay
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_PHOTOOVERLAY
diff --git a/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp b/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp
index f2f72dd..d856c6f 100644
--- a/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_PhotoOverlay"
 
+using namespace osgEarth_kml;
+
 void
 KML_PhotoOverlay::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Placemark b/src/osgEarthDrivers/kml/KML_Placemark
index 6984179..fb85875 100644
--- a/src/osgEarthDrivers/kml/KML_Placemark
+++ b/src/osgEarthDrivers/kml/KML_Placemark
@@ -21,6 +21,7 @@
 
 #include "KML_Feature"
 
+using namespace osgEarth_kml;
 using namespace osgEarth;
 
 struct KML_Placemark : public KML_Feature
diff --git a/src/osgEarthDrivers/kml/KML_Placemark.cpp b/src/osgEarthDrivers/kml/KML_Placemark.cpp
index add81e3..8e2c69b 100644
--- a/src/osgEarthDrivers/kml/KML_Placemark.cpp
+++ b/src/osgEarthDrivers/kml/KML_Placemark.cpp
@@ -30,6 +30,7 @@
 #include <osg/Depth>
 #include <osgDB/WriteFile>
 
+using namespace osgEarth_kml;
 using namespace osgEarth::Features;
 using namespace osgEarth::Annotation;
 
diff --git a/src/osgEarthDrivers/kml/KML_Point b/src/osgEarthDrivers/kml/KML_Point
index 698bf9f..b3a5b81 100644
--- a/src/osgEarthDrivers/kml/KML_Point
+++ b/src/osgEarthDrivers/kml/KML_Point
@@ -21,11 +21,15 @@
 
 #include "KML_Geometry"
 
-using namespace osgEarth;
-
-struct KML_Point : public KML_Geometry
+namespace osgEarth_kml
 {
-    virtual void parseCoords( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_Point : public KML_Geometry
+    {
+        virtual void parseCoords( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_POINT
diff --git a/src/osgEarthDrivers/kml/KML_Point.cpp b/src/osgEarthDrivers/kml/KML_Point.cpp
index 642859d..944dc6c 100644
--- a/src/osgEarthDrivers/kml/KML_Point.cpp
+++ b/src/osgEarthDrivers/kml/KML_Point.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_Point"
 
+using namespace osgEarth_kml;
+
 void
 KML_Point::parseCoords( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_PolyStyle b/src/osgEarthDrivers/kml/KML_PolyStyle
index 50d73c0..04907f8 100644
--- a/src/osgEarthDrivers/kml/KML_PolyStyle
+++ b/src/osgEarthDrivers/kml/KML_PolyStyle
@@ -21,11 +21,15 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_PolyStyle : public KML_Object
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, Style& style, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_PolyStyle : public KML_Object
+    {
+        virtual void scan( const Config& conf, Style& style, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_POLYSTYLE
diff --git a/src/osgEarthDrivers/kml/KML_PolyStyle.cpp b/src/osgEarthDrivers/kml/KML_PolyStyle.cpp
index 8228b92..a3fc042 100644
--- a/src/osgEarthDrivers/kml/KML_PolyStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_PolyStyle.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_PolyStyle"
 
+using namespace osgEarth_kml;
+
 void
 KML_PolyStyle::scan( const Config& conf, Style& style, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Polygon b/src/osgEarthDrivers/kml/KML_Polygon
index 7a24326..781f6c4 100644
--- a/src/osgEarthDrivers/kml/KML_Polygon
+++ b/src/osgEarthDrivers/kml/KML_Polygon
@@ -21,12 +21,16 @@
 
 #include "KML_Geometry"
 
-using namespace osgEarth;
-
-struct KML_Polygon : public KML_Geometry
+namespace osgEarth_kml
 {
-    virtual void parseStyle( const Config& conf, KMLContext& cx, Style& style);
-    virtual void parseCoords( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_Polygon : public KML_Geometry
+    {
+        virtual void parseStyle( const Config& conf, KMLContext& cx, Style& style);
+        virtual void parseCoords( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_POLYGON
diff --git a/src/osgEarthDrivers/kml/KML_Polygon.cpp b/src/osgEarthDrivers/kml/KML_Polygon.cpp
index a5c17b8..b70a978 100644
--- a/src/osgEarthDrivers/kml/KML_Polygon.cpp
+++ b/src/osgEarthDrivers/kml/KML_Polygon.cpp
@@ -20,6 +20,8 @@
 #include "KML_LinearRing"
 #include <iterator>
 
+using namespace osgEarth_kml;
+
 void
 KML_Polygon::parseStyle(const Config& conf, KMLContext& cx, Style& style)
 {
diff --git a/src/osgEarthDrivers/kml/KML_Root b/src/osgEarthDrivers/kml/KML_Root
index deb53bd..0ef7dee 100644
--- a/src/osgEarthDrivers/kml/KML_Root
+++ b/src/osgEarthDrivers/kml/KML_Root
@@ -21,13 +21,17 @@
 
 #include "KML_Object"
 
-struct KML_Root
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-    virtual void scan2( const Config& conf, KMLContext& cx );
-    virtual void build( const Config& conf, KMLContext& cx );
+    struct KML_Root
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+        virtual void scan2( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
 
-    virtual ~KML_Root() { }
-};
+        virtual ~KML_Root() { }
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_SCHEMA
diff --git a/src/osgEarthDrivers/kml/KML_Root.cpp b/src/osgEarthDrivers/kml/KML_Root.cpp
index 713866a..a80d0ee 100644
--- a/src/osgEarthDrivers/kml/KML_Root.cpp
+++ b/src/osgEarthDrivers/kml/KML_Root.cpp
@@ -26,6 +26,8 @@
 #include "KML_Placemark"
 #include "KML_NetworkLinkControl"
 
+using namespace osgEarth_kml;
+
 void 
 KML_Root::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Schema b/src/osgEarthDrivers/kml/KML_Schema
index 89d5ea9..1d79094 100644
--- a/src/osgEarthDrivers/kml/KML_Schema
+++ b/src/osgEarthDrivers/kml/KML_Schema
@@ -22,11 +22,15 @@
 #include "KML_Common"
 #include "KML_Container"
 
-using namespace osgEarth;
-
-struct KML_Schema : public KML_Object
+namespace osgEarth_kml
 {
+    using namespace osgEarth;
+
+    struct KML_Schema : public KML_Object
+    {
+
+    };
 
-};
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_SCHEMA
diff --git a/src/osgEarthDrivers/kml/KML_Schema.cpp b/src/osgEarthDrivers/kml/KML_Schema.cpp
index f9ac2b4..939c1e6 100644
--- a/src/osgEarthDrivers/kml/KML_Schema.cpp
+++ b/src/osgEarthDrivers/kml/KML_Schema.cpp
@@ -17,3 +17,5 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include "KML_Schema"
+
+using namespace osgEarth_kml;
diff --git a/src/osgEarthDrivers/kml/KML_ScreenOverlay b/src/osgEarthDrivers/kml/KML_ScreenOverlay
index 1ba32fb..6adb5e3 100644
--- a/src/osgEarthDrivers/kml/KML_ScreenOverlay
+++ b/src/osgEarthDrivers/kml/KML_ScreenOverlay
@@ -22,12 +22,16 @@
 #include "KML_Common"
 #include "KML_Overlay"
 
-using namespace osgEarth;
-
-struct KML_ScreenOverlay : public KML_Overlay
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-    virtual void build( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_ScreenOverlay : public KML_Overlay
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+        virtual void build( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_SCREENOVERLAY
diff --git a/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp b/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp
index 72643e2..e717a98 100644
--- a/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_ScreenOverlay"
 
+using namespace osgEarth_kml;
+
 void
 KML_ScreenOverlay::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_Style b/src/osgEarthDrivers/kml/KML_Style
index a28eed2..1a8d156 100644
--- a/src/osgEarthDrivers/kml/KML_Style
+++ b/src/osgEarthDrivers/kml/KML_Style
@@ -22,11 +22,15 @@
 #include "KML_Common"
 #include "KML_StyleSelector"
 
-using namespace osgEarth;
-
-struct KML_Style : public KML_StyleSelector
+namespace osgEarth_kml
 {
-    virtual void scan( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_Style : public KML_StyleSelector
+    {
+        virtual void scan( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_STYLE
diff --git a/src/osgEarthDrivers/kml/KML_Style.cpp b/src/osgEarthDrivers/kml/KML_Style.cpp
index dad6852..4d2c645 100644
--- a/src/osgEarthDrivers/kml/KML_Style.cpp
+++ b/src/osgEarthDrivers/kml/KML_Style.cpp
@@ -22,6 +22,8 @@
 #include "KML_LineStyle"
 #include "KML_PolyStyle"
 
+using namespace osgEarth_kml;
+
 void
 KML_Style::scan( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_StyleMap b/src/osgEarthDrivers/kml/KML_StyleMap
index 37438d7..3b421c1 100644
--- a/src/osgEarthDrivers/kml/KML_StyleMap
+++ b/src/osgEarthDrivers/kml/KML_StyleMap
@@ -22,11 +22,15 @@
 #include "KML_Common"
 #include "KML_StyleSelector"
 
-using namespace osgEarth;
-
-struct KML_StyleMap : public KML_StyleSelector
+namespace osgEarth_kml
 {
-    virtual void scan2( const Config& conf, KMLContext& cx );
-};
+    using namespace osgEarth;
+
+    struct KML_StyleMap : public KML_StyleSelector
+    {
+        virtual void scan2( const Config& conf, KMLContext& cx );
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_STYLEMAP
diff --git a/src/osgEarthDrivers/kml/KML_StyleMap.cpp b/src/osgEarthDrivers/kml/KML_StyleMap.cpp
index 9805657..7e5f308 100644
--- a/src/osgEarthDrivers/kml/KML_StyleMap.cpp
+++ b/src/osgEarthDrivers/kml/KML_StyleMap.cpp
@@ -18,6 +18,8 @@
  */
 #include "KML_StyleMap"
 
+using namespace osgEarth_kml;
+
 void
 KML_StyleMap::scan2( const Config& conf, KMLContext& cx )
 {
diff --git a/src/osgEarthDrivers/kml/KML_StyleSelector b/src/osgEarthDrivers/kml/KML_StyleSelector
index 23b51e8..286ae6e 100644
--- a/src/osgEarthDrivers/kml/KML_StyleSelector
+++ b/src/osgEarthDrivers/kml/KML_StyleSelector
@@ -21,10 +21,14 @@
 
 #include "KML_Object"
 
-using namespace osgEarth;
-
-struct KML_StyleSelector : public KML_Object
+namespace osgEarth_kml
 {
-};
+    using namespace osgEarth;
+
+    struct KML_StyleSelector : public KML_Object
+    {
+    };
+
+} // namespace osgEarth_kml
 
 #endif // OSGEARTH_DRIVER_KML_KML_STYLESELECTOR
diff --git a/src/osgEarthDrivers/kml/ReaderWriterKML.cpp b/src/osgEarthDrivers/kml/ReaderWriterKML.cpp
index 5a223e7..66a8d19 100644
--- a/src/osgEarthDrivers/kml/ReaderWriterKML.cpp
+++ b/src/osgEarthDrivers/kml/ReaderWriterKML.cpp
@@ -33,6 +33,7 @@
 
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
+using namespace osgEarth_kml;
 
 //---------------------------------------------------------------------------
 
diff --git a/src/osgEarthDrivers/model_simple/SimpleModelOptions b/src/osgEarthDrivers/model_simple/SimpleModelOptions
index 55b5433..9c44e5c 100644
--- a/src/osgEarthDrivers/model_simple/SimpleModelOptions
+++ b/src/osgEarthDrivers/model_simple/SimpleModelOptions
@@ -22,6 +22,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/ModelSource>
 #include <osgEarth/URI>
+#include <osgEarth/ShaderUtils>
 
 namespace osgEarth { namespace Drivers
 {
@@ -41,6 +42,9 @@ namespace osgEarth { namespace Drivers
 
         optional<osg::Vec3>& orientation() { return _orientation;}
         const optional<osg::Vec3>& orientation() const { return _orientation;}
+
+        optional<ShaderPolicy>& shaderPolicy() { return _shaderPolicy; }
+        const optional<ShaderPolicy>& shaderPolicy() const { return _shaderPolicy; }
         
         /**
          If specified, use this node instead try to load from url
@@ -51,7 +55,10 @@ namespace osgEarth { namespace Drivers
 
 
     public:
-        SimpleModelOptions( const ConfigOptions& options=ConfigOptions() ) : ModelSourceOptions( options ) {
+        SimpleModelOptions( const ConfigOptions& options=ConfigOptions() )
+            : ModelSourceOptions( options ),
+              _shaderPolicy( SHADERPOLICY_GENERATE )
+        {
             setDriver( "simple" );
             fromConfig( _conf );
         }
@@ -65,6 +72,11 @@ namespace osgEarth { namespace Drivers
             conf.updateIfSet( "lod_scale", _lod_scale );
             conf.updateIfSet( "location", _location );
             conf.updateIfSet( "orientation", _orientation);
+
+            conf.addIfSet( "shader_policy", "disable",  _shaderPolicy, SHADERPOLICY_DISABLE );
+            conf.addIfSet( "shader_policy", "inherit",  _shaderPolicy, SHADERPOLICY_INHERIT );
+            conf.addIfSet( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE );
+
             conf.updateNonSerializable( "SimpleModelOptions::Node", _node.get() );
             return conf;
         }
@@ -81,6 +93,11 @@ namespace osgEarth { namespace Drivers
             conf.getIfSet( "lod_scale", _lod_scale );
             conf.getIfSet( "location", _location);
             conf.getIfSet( "orientation", _orientation);
+
+            conf.getIfSet( "shader_policy", "disable",  _shaderPolicy, SHADERPOLICY_DISABLE );
+            conf.getIfSet( "shader_policy", "inherit",  _shaderPolicy, SHADERPOLICY_INHERIT );
+            conf.getIfSet( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE );
+
             _node = conf.getNonSerializable<osg::Node>( "SimpleModelOptions::Node" );
         }
 
@@ -88,6 +105,7 @@ namespace osgEarth { namespace Drivers
         optional<float> _lod_scale;
         optional<osg::Vec3> _location;
         optional<osg::Vec3> _orientation;
+        optional<ShaderPolicy> _shaderPolicy;
         osg::ref_ptr<osg::Node> _node;
     };
 
diff --git a/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp b/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp
index 0dfaed7..e4cb34d 100644
--- a/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp
+++ b/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp
@@ -155,8 +155,17 @@ public:
         // generate a shader program to render the model.
         if ( result.valid() )
         {
-            ShaderGenerator gen;
-            result->accept( gen );
+            if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
+            {
+                ShaderGenerator gen;
+                result->accept( gen );
+            }
+            else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
+            {
+                result->getOrCreateStateSet()->setAttributeAndModes(
+                    new osg::Program(),
+                    osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
+            }
         }
 
         return result.release();
diff --git a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer
index 8ff4421..e889b36 100644
--- a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer
+++ b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer
@@ -19,40 +19,44 @@
 #ifndef OSGEARTH_DRIVER_OCEAN_SURFACE_ELEV_PROXY_IMAGE_LAYER
 #define OSGEARTH_DRIVER_OCEAN_SURFACE_ELEV_PROXY_IMAGE_LAYER 1
 
-#include <osgEarth/Map>
+#include <osgEarth/MapFrame>
 #include <osgEarth/ImageLayer>
 
-using namespace osgEarth;
-
-/**
- * A customized ImageLayer that taps into another Map, reads elevation
- * tiles, and converts them into heightmap-encoded images.
- */
-class ElevationProxyImageLayer : public osgEarth::ImageLayer
+namespace osgEarth_ocean_surface
 {
-public:
+    using namespace osgEarth;
+
     /**
-     * Constucts a proxy layer
-     * @param sourceMap Map from which to read heightfields
+     * A customized ImageLayer that taps into another Map, reads elevation
+     * tiles, and converts them into heightmap-encoded images.
      */
-    ElevationProxyImageLayer( Map* sourceMap, const ImageLayerOptions& options );
+    class ElevationProxyImageLayer : public osgEarth::ImageLayer
+    {
+    public:
+        /**
+         * Constucts a proxy layer
+         * @param sourceMap Map from which to read heightfields
+         */
+        ElevationProxyImageLayer( Map* sourceMap, const ImageLayerOptions& options );
+
+        /** dtor */
+        virtual ~ElevationProxyImageLayer() { }
 
-    /** dtor */
-    virtual ~ElevationProxyImageLayer() { }
+    public: // ImageLayer
 
-public: // ImageLayer
+        virtual void initTileSource();
 
-    virtual void initTileSource();
+        virtual bool isKeyValid( const TileKey& key ) const;
 
-    virtual bool isKeyValid( const TileKey& key ) const;
+        virtual bool isCached( const TileKey& key ) const;
 
-    virtual bool isCached( const TileKey& key ) const;
+        virtual GeoImage createImage( const TileKey& key, ProgressCallback* progress, bool forceFallback );
 
-    virtual GeoImage createImage( const TileKey& key, ProgressCallback* progress, bool forceFallback );
+    private:
+        osg::observer_ptr<Map> _sourceMap;
+        MapFrame               _mapf;
+    };
 
-private:
-    osg::observer_ptr<Map> _sourceMap;
-    MapFrame               _mapf;
-};
+} // namespace osgEarth_ocean_surface
 
 #endif // OSGEARTH_DRIVER_OCEAN_SURFACE_ELEV_PROXY_IMAGE_LAYER
diff --git a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp
index 7082d52..365841f 100644
--- a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp
+++ b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp
@@ -18,6 +18,7 @@
  */
 #include "ElevationProxyImageLayer"
 
+using namespace osgEarth_ocean_surface;
 using namespace osgEarth;
 
 ElevationProxyImageLayer::ElevationProxyImageLayer( Map* sourceMap, const ImageLayerOptions& options ) :
@@ -32,6 +33,7 @@ void
 ElevationProxyImageLayer::initTileSource()
 {
     _tileSourceInitAttempted = true;
+    _tileSourceInitFailed    = true;
 }
 
 bool
diff --git a/src/osgEarthDrivers/ocean_surface/OceanCompositor b/src/osgEarthDrivers/ocean_surface/OceanCompositor
index bf456c9..a00126f 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanCompositor
+++ b/src/osgEarthDrivers/ocean_surface/OceanCompositor
@@ -21,31 +21,35 @@
 
 #include <osgEarth/TextureCompositor>
 
-using namespace osgEarth;
-
-/**
- * A custom texture compositor for rendering the ocean surface.
- */
-class OceanCompositor : public TextureCompositorTechnique
+namespace osgEarth_ocean_surface
 {
-public:
-    OceanCompositor() { }
+    using namespace osgEarth;
+
+    /**
+     * A custom texture compositor for rendering the ocean surface.
+     */
+    class OceanCompositor : public TextureCompositorTechnique
+    {
+    public:
+        OceanCompositor() { }
+
+        /** dtor */
+        virtual ~OceanCompositor() { }
 
-    /** dtor */
-    virtual ~OceanCompositor() { }
+        virtual bool requiresUnitTextureSpace() const { return true; }
 
-    virtual bool requiresUnitTextureSpace() const { return true; }
+        virtual bool usesShaderComposition() const { return true; }
 
-    virtual bool usesShaderComposition() const { return true; }
+        virtual void updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const;
 
-    virtual void updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const;
+        virtual void applyLayerUpdate(osg::StateSet*       stateSet,
+                                      UID                  layerUID,
+                                      const GeoImage&      preparedImage,
+                                      const TileKey&       tileKey,
+                                      const TextureLayout& layout,
+                                      osg::StateSet*       parentStateSet) const;
+    };
 
-    virtual void applyLayerUpdate(osg::StateSet*       stateSet,
-                                  UID                  layerUID,
-                                  const GeoImage&      preparedImage,
-                                  const TileKey&       tileKey,
-                                  const TextureLayout& layout,
-                                  osg::StateSet*       parentStateSet) const;
-};
+} // namespace osgEarth_ocean_surface
 
 #endif // OSGEARTH_DRIVER_OCEAN_SURFACE_COMPOSITOR
diff --git a/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp b/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp
index ee7c27f..ae87dc7 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp
+++ b/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp
@@ -19,10 +19,11 @@
 #include "OceanCompositor"
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osg/Texture2D>
 #include "OceanShaders"
 
+using namespace osgEarth_ocean_surface;
 using namespace osgEarth;
 
 void
@@ -36,9 +37,18 @@ OceanCompositor::updateMasterStateSet(osg::StateSet*       stateSet,
         vp->setName("osgEarth OceanCompositor");
         stateSet->setAttributeAndModes( vp, 1 );
     }
+    
     vp->installDefaultLightingShaders();
-    vp->setShader( "osgearth_vert_setupColoring", new osg::Shader(osg::Shader::VERTEX, source_setupColoring) );
-    vp->setShader( "osgearth_frag_applyColoring", new osg::Shader(osg::Shader::FRAGMENT, source_applyColoring ) );
+    
+    vp->setShader( 
+        "osgearth_vert_setupColoring", 
+        new osg::Shader(osg::Shader::VERTEX, source_setupColoring),
+        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+
+    vp->setShader( 
+        "osgearth_frag_applyColoring", 
+        new osg::Shader(osg::Shader::FRAGMENT, source_applyColoring),
+        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
 }
     
 namespace
diff --git a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer
index 9f317d5..1f4f551 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer
+++ b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer
@@ -28,31 +28,35 @@
 #include <osg/Image>
 #include <osg/Uniform>
 
-using namespace osgEarth;
-using namespace osgEarth::Drivers;
-
-class OceanSurfaceContainer : public osg::Group,
-                              public MapNodeObserver
+namespace osgEarth_ocean_surface
 {
-public:
-    OceanSurfaceContainer( MapNode* mapNode, const OceanSurfaceOptions& options );
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
+
+    class OceanSurfaceContainer : public osg::Group,
+                                  public MapNodeObserver
+    {
+    public:
+        OceanSurfaceContainer( MapNode* mapNode, const OceanSurfaceOptions& options );
+
+        /** dtor */
+        virtual ~OceanSurfaceContainer() { }
 
-    /** dtor */
-    virtual ~OceanSurfaceContainer() { }
+        void apply( const OceanSurfaceOptions& options );
 
-    void apply( const OceanSurfaceOptions& options );
+    public:
+        MapNode* getMapNode() { return _parentMapNode.get(); }
+        void setMapNode( MapNode* mapNode );
 
-public:
-    MapNode* getMapNode() { return _parentMapNode.get(); }
-    void setMapNode( MapNode* mapNode );
+    private:
+        osg::observer_ptr<MapNode> _parentMapNode;
+        OceanSurfaceOptions        _options;
+        osg::ref_ptr<osg::Uniform> _seaLevel, _lowFeather, _highFeather;
+        osg::ref_ptr<osg::Uniform> _baseColor;
 
-private:
-    osg::observer_ptr<MapNode> _parentMapNode;
-    OceanSurfaceOptions        _options;
-    osg::ref_ptr<osg::Uniform> _seaLevel, _lowFeather, _highFeather;
-    osg::ref_ptr<osg::Uniform> _baseColor;
+        void rebuild();
+    };
 
-    void rebuild();
-};
+} // namespace osgEarth_ocean_surface
 
 #endif // OSGEARTH_DRIVER_OCEAN_SURFACE_CONTAINER
diff --git a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp
index ccfbd0b..1c61ef2 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp
+++ b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp
@@ -20,10 +20,8 @@
 #include "OceanCompositor"
 #include "ElevationProxyImageLayer"
 #include <osgEarth/Map>
-#include <osgEarth/ShaderComposition>
 #include <osgEarth/TextureCompositor>
 #include <osgEarthDrivers/osg/OSGOptions>
-//#include <osgEarthDrivers/engine_osgterrain/OSGTerrainOptions>
 #include <osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions>
 
 #include <osg/CullFace>
@@ -32,6 +30,9 @@
 
 #define LC "[OceanSurface] "
 
+using namespace osgEarth_ocean_surface;
+
+
 OceanSurfaceContainer::OceanSurfaceContainer( MapNode* mapNode, const OceanSurfaceOptions& options ) :
 _parentMapNode( mapNode ),
 _options      ( options )
@@ -80,6 +81,7 @@ OceanSurfaceContainer::rebuild()
         // install an "elevation proxy" layer that reads elevation tiles from the
         // parent map and turns them into encoded images for our shader to use.
         ImageLayerOptions epo( "ocean-proxy" );
+        epo.cachePolicy() = CachePolicy::NO_CACHE;
         epo.maxLevel() = *_options.maxLOD();
         oceanMap->addImageLayer( new ElevationProxyImageLayer(_parentMapNode->getMap(), epo) );
 
diff --git a/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp b/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp
index 4def40f..37d7414 100644
--- a/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp
+++ b/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp
@@ -29,6 +29,7 @@
 #undef  LC
 #define LC "[ReaderWriterOceanSurface] "
 
+using namespace osgEarth_ocean_surface;
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 
diff --git a/src/osgEarthDrivers/osg/OSGTileSource.cpp b/src/osgEarthDrivers/osg/OSGTileSource.cpp
index 030fa3e..b4a0846 100644
--- a/src/osgEarthDrivers/osg/OSGTileSource.cpp
+++ b/src/osgEarthDrivers/osg/OSGTileSource.cpp
@@ -143,7 +143,7 @@ public:
         if ( !_image.valid() || key.getLevelOfDetail() > getMaxDataLevel() )
             return NULL;
 
-        GeoImage cropped = _image.crop( key.getExtent(), true, getPixelsPerTile(), getPixelsPerTile() );
+        GeoImage cropped = _image.crop( key.getExtent(), true, getPixelsPerTile(), getPixelsPerTile(), *_options.bilinearReprojection() );
         return cropped.valid() ? cropped.takeImage() : 0L;
     }
 
diff --git a/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp b/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp
index ddf6aad..cac28c5 100644
--- a/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp
+++ b/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp
@@ -955,26 +955,8 @@ ExtrudeGeometryFilter::push( FeatureList& input, FilterContext& context )
             groupStateSet->setAttributeAndModes( new osg::LineWidth(*_outlineSymbol->stroke()->width()), 1 );
     }
 
-#if 0
-    // if we have textures, install a shader to draw them
-    if ( _wallSkinSymbol.valid() || _roofSkinSymbol.valid() )
-    {
-        osg::StateSet* stateSet = group->getOrCreateStateSet();
-
-        VirtualProgram* vp = new VirtualProgram();
-        vp->setName("ExtrudeGeomFilter");
-        vp->installDefaultColoringShaders( 1 );
-        stateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
-
-        // a default empty texture will support any non-textured geometry 
-        osg::Texture2D* tex = new osg::Texture2D( ImageUtils::createEmptyImage() );
-        tex->setUnRefImageDataAfterApply( false );
-        stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON);
-        stateSet->getOrCreateUniform("tex0", osg::Uniform::SAMPLER_2D)->set(0);
-    }
-#endif
+#if 0 // now called from GeometryCompiler
 
-#if 1
     // generate shaders to draw the geometry.
     if ( Registry::capabilities().supportsGLSL() )
     {
diff --git a/src/osgEarthFeatures/Feature.cpp b/src/osgEarthFeatures/Feature.cpp
index a84b0b9..7541182 100644
--- a/src/osgEarthFeatures/Feature.cpp
+++ b/src/osgEarthFeatures/Feature.cpp
@@ -437,7 +437,24 @@ Feature::getGeoJSON()
     {
 
         for (AttributeTable::const_iterator itr = getAttrs().begin(); itr != getAttrs().end(); ++itr)
-            props[itr->first] = itr->second.getString();
+        {
+            if (itr->second.first == ATTRTYPE_INT)
+            {
+                props[itr->first] = itr->second.getInt();
+            }
+            else if (itr->second.first == ATTRTYPE_DOUBLE)
+            {
+                props[itr->first] = itr->second.getDouble();
+            }
+            else if (itr->second.first == ATTRTYPE_BOOL)
+            {
+                props[itr->first] = itr->second.getBool();
+            }
+            else
+            {
+                props[itr->first] = itr->second.getString();
+            }            
+        }
     } 
 
     root["properties"] = props;
@@ -486,4 +503,4 @@ void Feature::transform( const SpatialReference* srs )
         getSRS()->transform( geom->asVector(), srs );
     }
     setSRS( srs );
-}
\ No newline at end of file
+}
diff --git a/src/osgEarthFeatures/FeatureModelGraph b/src/osgEarthFeatures/FeatureModelGraph
index 941c843..9a5882d 100644
--- a/src/osgEarthFeatures/FeatureModelGraph
+++ b/src/osgEarthFeatures/FeatureModelGraph
@@ -74,6 +74,11 @@ namespace osgEarth { namespace Features
         void setStyles( StyleSheet* styles );
 
         /**
+         * Session associated with this feature graph.
+         */
+        Session* getSession() { return _session; }
+
+        /**
          * Mark the feature graph dirty and in need of regeneration 
          */
         void dirty();
@@ -85,6 +90,10 @@ namespace osgEarth { namespace Features
          */
         void addPostMergeOperation( NodeOperation* op );
 
+        /**
+         * Access to the features levels
+         */
+        const std::vector<const FeatureLevel*>& getLevels() const { return _lodmap; };
 
     public: // osg::Node
 
@@ -142,8 +151,6 @@ namespace osgEarth { namespace Features
 
         void redraw();
 
-        void installShaderMains();
-
     private:
         FeatureModelSourceOptions        _options;
         osg::ref_ptr<FeatureNodeFactory> _factory;
diff --git a/src/osgEarthFeatures/FeatureModelGraph.cpp b/src/osgEarthFeatures/FeatureModelGraph.cpp
index 18065df..66b5b74 100644
--- a/src/osgEarthFeatures/FeatureModelGraph.cpp
+++ b/src/osgEarthFeatures/FeatureModelGraph.cpp
@@ -27,7 +27,6 @@
 #include <osgEarth/FadeEffect>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/ShaderComposition>
 #include <osgEarth/ThreadingUtils>
 
 #include <osg/CullFace>
@@ -124,9 +123,16 @@ struct osgEarthFeatureModelPseudoLoader : public osgDB::ReaderWriter
 
         osg::ref_ptr<FeatureModelGraph> graph = getGraph(uid);
         if ( graph.valid() )
-            return ReadResult( graph->load( lod, x, y, uri ) );
-        else
-            return ReadResult::ERROR_IN_READING_FILE;
+        {
+            // Take a reference on the map to avoid map destruction during thread operation
+            osg::ref_ptr<const Map> map = graph->getSession()->getMap();
+            if (map.valid() == true)
+            {
+                return ReadResult( graph->load( lod, x, y, uri ) );
+            }
+        }
+
+        return ReadResult::ERROR_IN_READING_FILE;
     }
 
     static UID registerGraph( FeatureModelGraph* graph )
@@ -263,18 +269,25 @@ _pendingUpdate( false )
         }
     }
 
-    // install base shader mains.
-    if ( Registry::instance()->getCapabilities().supportsGLSL() )
-    {
-        installShaderMains();
-    }
+    // Apply some default state. The options properties let you override the
+    // defaults, but we'll set some reasonable state if they are not set.
 
-    // Set up the state set.
-    // backface culling is ON by default. By the way, this is most definitely
-    // necessary when shading with shadows.
     osg::StateSet* stateSet = getOrCreateStateSet();
-    stateSet->setMode( GL_CULL_FACE, 1 );
-    stateSet->setMode( GL_BLEND, 1 );
+
+    // Set up backface culling. If the option is unset, enable it by default
+    // since shadowing requires it and it's a decent general-purpose setting
+    if ( _options.backfaceCulling().isSet() )
+        stateSet->setMode( GL_CULL_FACE, *_options.backfaceCulling() ? 1 : 0 );
+    else
+        stateSet->setMode( GL_CULL_FACE, 1 );
+
+    // Set up alpha blending. Enable it by default if not specified.
+    if ( _options.alphaBlending().isSet() )
+        stateSet->setMode( GL_BLEND, *_options.alphaBlending() ? 1 : 0 );
+    else
+        stateSet->setMode( GL_BLEND, 1 );
+
+    // Set up lighting, only if the option is set
     if ( _options.enableLighting().isSet() )
         stateSet->setMode( GL_LIGHTING, *_options.enableLighting() ? 1 : 0 );
 
@@ -297,12 +310,6 @@ FeatureModelGraph::~FeatureModelGraph()
 }
 
 void
-FeatureModelGraph::installShaderMains()
-{
-    osg::StateSet* ss = this->getOrCreateStateSet();
-}
-
-void
 FeatureModelGraph::dirty()
 {
     _dirty = true;
diff --git a/src/osgEarthFeatures/FeatureModelSource b/src/osgEarthFeatures/FeatureModelSource
index dbc85a1..d55024b 100644
--- a/src/osgEarthFeatures/FeatureModelSource
+++ b/src/osgEarthFeatures/FeatureModelSource
@@ -72,6 +72,14 @@ namespace osgEarth { namespace Features
         optional<bool>& featureIndexing() { return _featureIndexing; }
         const optional<bool>& featureIndexing() const { return _featureIndexing; }
 
+        /** Whether to activate backface culling (default = yes) */
+        optional<bool>& backfaceCulling() { return _backfaceCulling; }
+        const optional<bool>& backfaceCulling() const { return _backfaceCulling; }
+
+        /** Whether to activate alpha blending (default = yes) */
+        optional<bool>& alphaBlending() { return _alphaBlending; }
+        const optional<bool>& alphaBlending() const { return _alphaBlending; }
+
         /** Explicity caching policy for data from the underlying feature source */
         optional<CachePolicy>& cachePolicy() { return _cachePolicy; }
         const optional<CachePolicy>& cachePolicy() const { return _cachePolicy; }
@@ -115,6 +123,8 @@ namespace osgEarth { namespace Features
         optional<bool>                  _mergeGeometry;
         optional<bool>                  _clusterCulling;
         optional<bool>                  _featureIndexing;
+        optional<bool>                  _backfaceCulling;
+        optional<bool>                  _alphaBlending;
         optional<CachePolicy>           _cachePolicy;
         optional<float>                 _fadeInDuration;
 
diff --git a/src/osgEarthFeatures/FeatureModelSource.cpp b/src/osgEarthFeatures/FeatureModelSource.cpp
index 3274c3e..c15af29 100644
--- a/src/osgEarthFeatures/FeatureModelSource.cpp
+++ b/src/osgEarthFeatures/FeatureModelSource.cpp
@@ -37,6 +37,8 @@ _maxGranularity_deg( 1.0 ),
 _mergeGeometry     ( false ),
 _clusterCulling    ( true ),
 _featureIndexing   ( false ),
+_backfaceCulling   ( true ),
+_alphaBlending     ( true ),
 _fadeInDuration    ( 0.0f )
 {
     fromConfig( _conf );
@@ -59,6 +61,8 @@ FeatureModelSourceOptions::fromConfig( const Config& conf )
     conf.getIfSet( "merge_geometry",   _mergeGeometry );
     conf.getIfSet( "cluster_culling",  _clusterCulling );
     conf.getIfSet( "feature_indexing", _featureIndexing );
+    conf.getIfSet( "backface_culling", _backfaceCulling );
+    conf.getIfSet( "alpha_blending",   _alphaBlending );
     conf.getIfSet( "fade_in_duration", _fadeInDuration );
 
     std::string gt = conf.value( "geometry_type" );
@@ -89,6 +93,8 @@ FeatureModelSourceOptions::getConfig() const
     conf.updateIfSet( "merge_geometry",   _mergeGeometry );
     conf.updateIfSet( "cluster_culling",  _clusterCulling );
     conf.updateIfSet( "feature_indexing", _featureIndexing );
+    conf.updateIfSet( "backface_culling", _backfaceCulling );
+    conf.updateIfSet( "alpha_blending",   _alphaBlending );
     conf.updateIfSet( "fade_in_duration", _fadeInDuration );
 
     if ( _geomTypeOverride.isSet() ) {
diff --git a/src/osgEarthFeatures/FeatureSource b/src/osgEarthFeatures/FeatureSource
index 11a1e10..9f5726d 100644
--- a/src/osgEarthFeatures/FeatureSource
+++ b/src/osgEarthFeatures/FeatureSource
@@ -32,6 +32,7 @@
 #include <osgEarth/Cache>
 #include <osgEarth/CachePolicy>
 #include <osgEarth/URI>
+#include <osgEarth/Revisioning>
 
 #include <osgDB/ReaderWriter>
 #include <OpenThreads/Mutex>
diff --git a/src/osgEarthFeatures/Filter b/src/osgEarthFeatures/Filter
index 272810b..8aad80f 100644
--- a/src/osgEarthFeatures/Filter
+++ b/src/osgEarthFeatures/Filter
@@ -114,10 +114,10 @@ namespace osgEarth { namespace Features
     };
 
 #define OSGEARTH_REGISTER_FEATUREFILTER( CLASSNAME )\
-    static RegisterFeatureFilterProxy<CLASSNAME> s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME;
+    static osgEarth::Features::RegisterFeatureFilterProxy<CLASSNAME> s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME;
 
 #define OSGEARTH_REGISTER_SIMPLE_FEATUREFILTER( KEY, CLASSNAME)\
-    static RegisterFeatureFilterProxy< SimpleFeatureFilterFactory<CLASSNAME> > s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME##KEY(new SimpleFeatureFilterFactory<CLASSNAME>(#KEY));
+    static osgEarth::Features::RegisterFeatureFilterProxy< osgEarth::Features::SimpleFeatureFilterFactory<CLASSNAME> > s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME##KEY(new osgEarth::Features::SimpleFeatureFilterFactory<CLASSNAME>(#KEY));
 
 
     template<typename T>
diff --git a/src/osgEarthFeatures/GeometryCompiler b/src/osgEarthFeatures/GeometryCompiler
index dcb7b6f..8289c8a 100644
--- a/src/osgEarthFeatures/GeometryCompiler
+++ b/src/osgEarthFeatures/GeometryCompiler
@@ -77,6 +77,9 @@ namespace osgEarth { namespace Features
         optional<bool>& useVertexBufferObjects() { return _useVertexBufferObjects;}
         const optional<bool>& useVertexBufferObjects() const { return _useVertexBufferObjects;}
 
+        optional<ShaderPolicy>& shaderPolicy() { return _shaderPolicy; }
+        const optional<ShaderPolicy>& shaderPolicy() const { return _shaderPolicy; }
+
 
     public:
         Config getConfig() const;
@@ -93,6 +96,7 @@ namespace osgEarth { namespace Features
         optional<double>               _resampleMaxLength;
         optional<bool>                 _ignoreAlt;
         optional<bool>                 _useVertexBufferObjects;
+        optional<ShaderPolicy>         _shaderPolicy;
 
         void fromConfig( const Config& conf );
     };
diff --git a/src/osgEarthFeatures/GeometryCompiler.cpp b/src/osgEarthFeatures/GeometryCompiler.cpp
index 6b3342b..957d905 100644
--- a/src/osgEarthFeatures/GeometryCompiler.cpp
+++ b/src/osgEarthFeatures/GeometryCompiler.cpp
@@ -25,6 +25,10 @@
 #include <osgEarthFeatures/ScatterFilter>
 #include <osgEarthFeatures/SubstituteModelFilter>
 #include <osgEarthFeatures/TessellateOperator>
+#include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
+#include <osgEarth/ShaderGenerator>
+#include <osgEarth/ShaderUtils>
 #include <osg/MatrixTransform>
 #include <osg/Timer>
 #include <osgDB/WriteFile>
@@ -42,6 +46,7 @@ namespace
     osg::ref_ptr<PointSymbol>   s_defaultPointSymbol   = new PointSymbol();
     osg::ref_ptr<LineSymbol>    s_defaultLineSymbol    = new LineSymbol();
     osg::ref_ptr<PolygonSymbol> s_defaultPolygonSymbol = new PolygonSymbol();
+    osg::ref_ptr<osg::Program>  s_nullProgram          = new osg::Program();
 }
 
 //-----------------------------------------------------------------------
@@ -53,7 +58,8 @@ _mergeGeometry     ( false ),
 _clustering        ( false ),
 _instancing        ( false ),
 _ignoreAlt         ( false ),
-_useVertexBufferObjects( true )
+_useVertexBufferObjects( true ),
+_shaderPolicy      ( SHADERPOLICY_GENERATE )
 {
     fromConfig(_conf);
 }
@@ -70,6 +76,10 @@ GeometryCompilerOptions::fromConfig( const Config& conf )
     conf.getIfSet   ( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE );
     conf.getIfSet   ( "geo_interpolation", "rhumb_line",   _geoInterp, GEOINTERP_RHUMB_LINE );
     conf.getIfSet   ( "use_vbo", _useVertexBufferObjects);
+
+    conf.getIfSet( "shader_policy", "disable",  _shaderPolicy, SHADERPOLICY_DISABLE );
+    conf.getIfSet( "shader_policy", "inherit",  _shaderPolicy, SHADERPOLICY_INHERIT );
+    conf.getIfSet( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE );
 }
 
 Config
@@ -85,6 +95,10 @@ GeometryCompilerOptions::getConfig() const
     conf.addIfSet   ( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE );
     conf.addIfSet   ( "geo_interpolation", "rhumb_line",   _geoInterp, GEOINTERP_RHUMB_LINE );
     conf.addIfSet   ( "use_vbo", _useVertexBufferObjects);
+
+    conf.addIfSet( "shader_policy", "disable",  _shaderPolicy, SHADERPOLICY_DISABLE );
+    conf.addIfSet( "shader_policy", "inherit",  _shaderPolicy, SHADERPOLICY_INHERIT );
+    conf.addIfSet( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE );
     return conf;
 }
 
@@ -417,18 +431,32 @@ GeometryCompiler::compile(FeatureList&          workingSet,
         }
     }
 
-    // Finally, optimize the stateset-sharing in the group.
+    // Common state set cache?
+    osg::ref_ptr<StateSetCache> sscache;
     if ( sharedCX.getSession() )
+        sscache = sharedCX.getSession()->getStateSetCache();
+    else 
+        sscache = new StateSetCache();
+
+    // Generate shaders, if necessary
+    if (Registry::capabilities().supportsGLSL())
     {
-        sharedCX.getSession()->getStateSetCache()->optimize( resultGroup.get() );
-        //OE_INFO << LC << "state set cache size = " << sharedCX.getSession()->getStateSetCache()->size() << std::endl;
-    }
-    else
-    {
-        StateSetCache tempCache;
-        tempCache.optimize( resultGroup.get() );
+        if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
+        {
+            ShaderGenerator gen( 0L );
+            resultGroup->accept( gen );
+        }
+        else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
+        {
+            resultGroup->getOrCreateStateSet()->setAttributeAndModes(
+                s_nullProgram,
+                osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
+        }
     }
 
+    // Optimize stateset sharing.
+    sscache->optimize( resultGroup.get() );
+
 
     //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" );
 
diff --git a/src/osgEarthFeatures/Session b/src/osgEarthFeatures/Session
index 1b39387..921b60a 100644
--- a/src/osgEarthFeatures/Session
+++ b/src/osgEarthFeatures/Session
@@ -25,6 +25,8 @@
 #include <osgEarthSymbology/StyleSheet>
 #include <osgEarth/StateSetCache>
 #include <osgEarth/ThreadingUtils>
+#include <osgEarth/MapInfo>
+#include <osgEarth/MapFrame>
 #include <osgEarth/Map>
 
 namespace osgEarth { namespace Features
@@ -72,6 +74,11 @@ namespace osgEarth { namespace Features
          */
         const MapInfo& getMapInfo() const { return _mapInfo; }
 
+        /**
+         * Gets the map related to this session. Be carefull, this is held by an osg::observer_ptr and may be NULL
+         */
+        const Map* getMap() const { return _map.get(); }
+
         /** The style sheet governing this session. */
         void setStyles( StyleSheet* value );
         StyleSheet* styles() const { return _styles.get(); }
diff --git a/src/osgEarthFeatures/SubstituteModelFilter.cpp b/src/osgEarthFeatures/SubstituteModelFilter.cpp
index 4640c9e..d92baf8 100644
--- a/src/osgEarthFeatures/SubstituteModelFilter.cpp
+++ b/src/osgEarthFeatures/SubstituteModelFilter.cpp
@@ -20,7 +20,7 @@
 #include <osgEarthFeatures/FeatureSourceIndexNode>
 #include <osgEarthSymbology/MeshConsolidator>
 #include <osgEarth/ECEF>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/ShaderGenerator>
 #include <osgEarth/DrawInstanced>
 #include <osgEarth/Registry>
@@ -273,11 +273,14 @@ SubstituteModelFilter::process(const FeatureList&           features,
         attachPoint->getOrCreateStateSet()->setAttributeAndModes( p, osg::StateAttribute::ON );
     }
 
+#if 0 // now called from GeometryCompiler
+
     // Generate shader code to render the models
     StateSetCache* cache = context.getSession() ? context.getSession()->getStateSetCache() : 0L;
     ShaderGenerator gen( cache );
     attachPoint->accept( gen );
 
+#endif
     return true;
 }
 
diff --git a/src/osgEarthFeatures/TessellateOperator b/src/osgEarthFeatures/TessellateOperator
index 959bfa8..e3f43dc 100644
--- a/src/osgEarthFeatures/TessellateOperator
+++ b/src/osgEarthFeatures/TessellateOperator
@@ -32,6 +32,29 @@ namespace osgEarth { namespace Features
      */
     class OSGEARTHFEATURES_EXPORT TessellateOperator
     {
+    public: // static methods
+
+        /**
+         * Tessellates a straight line into multiple segments.
+         */
+        static void tessellateLinear( 
+            const osg::Vec3d&        p0, 
+            const osg::Vec3d&        p1, 
+            unsigned                 parts, 
+            std::vector<osg::Vec3d>& out );
+
+        /**
+         * Tessellates a geographic line (great circle or rhumb) between
+         * two geographic points (long,lat,alt) into multiple segments along
+         * a spheroid.
+         */
+        static void tessellateGeo( 
+            const osg::Vec3d&        p0, 
+            const osg::Vec3d&        p1, 
+            unsigned                 parts, 
+            GeoInterpolation         interp,
+            std::vector<osg::Vec3d>& out );
+
     public:
         /**
          * Constructs a new tessellation operator.
diff --git a/src/osgEarthFeatures/TessellateOperator.cpp b/src/osgEarthFeatures/TessellateOperator.cpp
index 7d04536..3665a13 100644
--- a/src/osgEarthFeatures/TessellateOperator.cpp
+++ b/src/osgEarthFeatures/TessellateOperator.cpp
@@ -25,56 +25,55 @@ using namespace osgEarth::Symbology;
 
 //------------------------------------------------------------------------
 
-namespace
+void 
+TessellateOperator::tessellateLinear( const osg::Vec3d& p0, const osg::Vec3d& p1, unsigned parts, Vec3dVector& out )
 {
-    void tessellateLinear( const osg::Vec3d& p0, const osg::Vec3d& p1, unsigned parts, Vec3dVector& out )
+    osg::Vec3d vec = (p1-p0)/double(parts);
+    out.push_back( p0 );
+    for( unsigned i=1; i<parts; ++i )
     {
-        osg::Vec3d vec = (p1-p0)/double(parts);
-        out.push_back( p0 );
-        for( unsigned i=1; i<parts; ++i )
-        {
-            out.push_back( p0 + vec*double(i) );
-        }
+        out.push_back( p0 + vec*double(i) );
     }
+}
 
-    void tessellateGeo( const osg::Vec3d& p0, const osg::Vec3d& p1, unsigned parts, GeoInterpolation interp, Vec3dVector& out )
-    {
-        double step = 1.0/double(parts);
-        double zdelta = p1.z() - p0.z();
-
-        out.push_back( p0 );
+void 
+TessellateOperator::tessellateGeo( const osg::Vec3d& p0, const osg::Vec3d& p1, unsigned parts, GeoInterpolation interp, Vec3dVector& out )
+{
+    double step = 1.0/double(parts);
+    double zdelta = p1.z() - p0.z();
 
-        for( unsigned i=1; i<parts; ++i )
-        {
-            double t = step*double(i);
-            osg::Vec3d p;
+    out.push_back( p0 );
 
-            if ( interp == GEOINTERP_GREAT_CIRCLE )
-            {
-                double lat, lon;
-                GeoMath::interpolate(
-                    osg::DegreesToRadians(p0.y()), osg::DegreesToRadians(p0.x()),
-                    osg::DegreesToRadians(p1.y()), osg::DegreesToRadians(p1.x()),
-                    t,
-                    lat, lon );
-                p.set( osg::RadiansToDegrees(lon), osg::RadiansToDegrees(lat), p0.z() + t*zdelta );
-            }
-            else // GEOINTERP_RHUMB_LINE
-            {
-                double lat1 = osg::DegreesToRadians(p0.y()), lon1 = osg::DegreesToRadians(p1.x());
-                double lat2 = osg::DegreesToRadians(p1.y()), lon2 = osg::DegreesToRadians(p1.x());
+    for( unsigned i=1; i<parts; ++i )
+    {
+        double t = step*double(i);
+        osg::Vec3d p;
 
-                double distance = GeoMath::rhumbDistance( lat1, lon1, lat2, lon2 );
-                double bearing  = GeoMath::rhumbBearing( lat1, lon1, lat2, lon2 );
+        if ( interp == GEOINTERP_GREAT_CIRCLE )
+        {
+            double lat, lon;
+            GeoMath::interpolate(
+                osg::DegreesToRadians(p0.y()), osg::DegreesToRadians(p0.x()),
+                osg::DegreesToRadians(p1.y()), osg::DegreesToRadians(p1.x()),
+                t,
+                lat, lon );
+            p.set( osg::RadiansToDegrees(lon), osg::RadiansToDegrees(lat), p0.z() + t*zdelta );
+        }
+        else // GEOINTERP_RHUMB_LINE
+        {
+            double lat1 = osg::DegreesToRadians(p0.y()), lon1 = osg::DegreesToRadians(p1.x());
+            double lat2 = osg::DegreesToRadians(p1.y()), lon2 = osg::DegreesToRadians(p1.x());
 
-                double lat3, lon3;
-                GeoMath::rhumbDestination(lat1, lon1, bearing, distance, lat3, lon3);
+            double distance = GeoMath::rhumbDistance( lat1, lon1, lat2, lon2 );
+            double bearing  = GeoMath::rhumbBearing( lat1, lon1, lat2, lon2 );
 
-                p.set( osg::RadiansToDegrees(lon3), osg::RadiansToDegrees(lat3), p0.z() + t*zdelta );
-            }
+            double lat3, lon3;
+            GeoMath::rhumbDestination(lat1, lon1, bearing, distance, lat3, lon3);
 
-            out.push_back(p);
+            p.set( osg::RadiansToDegrees(lon3), osg::RadiansToDegrees(lat3), p0.z() + t*zdelta );
         }
+
+        out.push_back(p);
     }
 }
 
diff --git a/src/osgEarthQt/AnnotationDialogs.cpp b/src/osgEarthQt/AnnotationDialogs.cpp
index 8325fa9..af877cd 100644
--- a/src/osgEarthQt/AnnotationDialogs.cpp
+++ b/src/osgEarthQt/AnnotationDialogs.cpp
@@ -1082,4 +1082,4 @@ void AddEllipseDialog::onFillColorButtonClicked()
 
     updateButtonColorStyle(_fillColorButton, color);
   }
-}
\ No newline at end of file
+}
diff --git a/src/osgEarthQt/AnnotationToolbar.cpp b/src/osgEarthQt/AnnotationToolbar.cpp
index 8b5ee89..4b9c0f6 100644
--- a/src/osgEarthQt/AnnotationToolbar.cpp
+++ b/src/osgEarthQt/AnnotationToolbar.cpp
@@ -175,4 +175,4 @@ void AnnotationToolbar::onAddFinished(int result)
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/osgEarthQt/DataManager.cpp b/src/osgEarthQt/DataManager.cpp
index d0a9b7a..6dd6f47 100644
--- a/src/osgEarthQt/DataManager.cpp
+++ b/src/osgEarthQt/DataManager.cpp
@@ -19,6 +19,7 @@
 #include <osgEarthQt/DataManager>
 
 #include <osgEarth/Map>
+#include <osgEarth/MapModelChange>
 #include <osgEarth/MapNode>
 
 using namespace osgEarth::QtGui;
diff --git a/src/osgEarthQt/TerrainProfileWidget.cpp b/src/osgEarthQt/TerrainProfileWidget.cpp
index ac78b4d..fdeb971 100644
--- a/src/osgEarthQt/TerrainProfileWidget.cpp
+++ b/src/osgEarthQt/TerrainProfileWidget.cpp
@@ -285,4 +285,4 @@ void TerrainProfileWidget::drawProfileLine()
   }
 
   refreshViews();
-}
\ No newline at end of file
+}
diff --git a/src/osgEarthSymbology/Expression.cpp b/src/osgEarthSymbology/Expression.cpp
index 16a7cce..2cc18c1 100644
--- a/src/osgEarthSymbology/Expression.cpp
+++ b/src/osgEarthSymbology/Expression.cpp
@@ -78,18 +78,53 @@ NumericExpression::init()
 {
     _vars.clear();
     _rpn.clear();
-    StringTokenizer tokenizer( "", "" );
-    tokenizer.addDelims( "[],()%*/+-", true );
-    tokenizer.addQuotes( "'\"", true );
-    tokenizer.keepEmpties() = false;
+
+    StringTokenizer variablesTokenizer( "", "" );
+    variablesTokenizer.addDelims( "[]", true );
+    variablesTokenizer.addQuotes( "'\"", true );
+    variablesTokenizer.keepEmpties() = false;
+
+    StringTokenizer operandTokenizer( "", "" );
+    operandTokenizer.addDelims( ",()%*/+-", true );
+    operandTokenizer.addQuotes( "'\"", true );
+    operandTokenizer.keepEmpties() = false;
+
+    StringVector variablesTokens;
+    variablesTokenizer.tokenize( _src, variablesTokens );
 
     StringVector t;
-    tokenizer.tokenize( _src, t );
-    //tokenize(_src, t, "[],()%*/+-", "'\"", false, true);
+    bool invar = false;
+    for( unsigned i=0; i<variablesTokens.size(); ++i )
+    {
+        if ( variablesTokens[i] == "[" && !invar )
+        {
+            // Start variable, add "[" token
+            invar = true;
+            t.push_back(variablesTokens[i]);
+        }
+        else if ( variablesTokens[i] == "]" && invar )
+        {
+            // End variable, add "]" token
+            invar = false;
+            t.push_back(variablesTokens[i]);
+        }
+        else if ( invar )
+        {
+            // Variable, add variable token
+            t.push_back(variablesTokens[i]);
+        }
+        else
+        {
+            // Operands, tokenize it and add tokens
+            StringVector operandTokens;
+            operandTokenizer.tokenize( variablesTokens[i], operandTokens );
+            t.insert(t.end(), operandTokens.begin(), operandTokens.end());
+        }
+    }
 
     // identify tokens:
     AtomVector infix;
-    bool invar = false;
+    invar = false;
     for( unsigned i=0; i<t.size(); ++i ) {
         if ( t[i] == "[" && !invar ) {
             invar = true;
diff --git a/src/osgEarthSymbology/SLD.cpp b/src/osgEarthSymbology/SLD.cpp
index b304912..47ba905 100644
--- a/src/osgEarthSymbology/SLD.cpp
+++ b/src/osgEarthSymbology/SLD.cpp
@@ -148,6 +148,11 @@ SLDReader::readStyleFromCSSParams( const Config& conf, Style& sc )
             if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
             text->halo()->color() = htmlColorToVec4f( value );
         }
+        else if ( match(key, "text-halo-offset") )
+        {
+            if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
+            text->haloOffset() = as<float>(value, 0.07f);
+        }
         else if ( match(key, "text-remove-duplicate-labels") )
         {
             if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
diff --git a/src/osgEarthSymbology/Skins.cpp b/src/osgEarthSymbology/Skins.cpp
index 900508f..26b77bc 100644
--- a/src/osgEarthSymbology/Skins.cpp
+++ b/src/osgEarthSymbology/Skins.cpp
@@ -19,7 +19,6 @@
 #include <osgEarthSymbology/Skins>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ImageUtils>
-#include <osgEarth/ShaderComposition>
 #include <osgEarth/Registry>
 
 #include <osg/BlendFunc>
diff --git a/src/osgEarthSymbology/TextSymbol b/src/osgEarthSymbology/TextSymbol
index bd9c6dd..2b511b7 100644
--- a/src/osgEarthSymbology/TextSymbol
+++ b/src/osgEarthSymbology/TextSymbol
@@ -33,12 +33,12 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT TextSymbol : public Symbol
     {
     public:
-		enum Encoding {
+        enum Encoding {
             ENCODING_ASCII,
             ENCODING_UTF8,
             ENCODING_UTF16,
-			ENCODING_UTF32
-		};
+            ENCODING_UTF32
+        };
 
         // note: these are identical to the values in osgText::Text::AlignmentType
         enum Alignment {
@@ -78,6 +78,10 @@ namespace osgEarth { namespace Symbology
         optional<Stroke>& halo() { return _halo; }
         const optional<Stroke>& halo() const { return _halo; }
 
+        /** Text outline offset */
+        optional<float>& haloOffset() { return _haloOffset; }
+        const optional<float>& haloOffset() const { return _haloOffset; }
+
         /** Name of text font. */
         optional<std::string>& font() { return _font; }
         const optional<std::string>& font() const { return _font; }
@@ -125,6 +129,7 @@ namespace osgEarth { namespace Symbology
     protected:
         optional<Fill>              _fill;
         optional<Stroke>            _halo;
+        optional<float>             _haloOffset;
         optional<std::string>       _font;
         optional<float>             _size;
         optional<StringExpression>  _content;
@@ -132,7 +137,7 @@ namespace osgEarth { namespace Symbology
         optional<bool>              _removeDuplicateLabels;
         optional<osg::Vec2s>        _pixelOffset;
         optional<std::string>       _provider;
-		optional<Encoding>          _encoding;
+        optional<Encoding>          _encoding;
         optional<Alignment>         _alignment;
         optional<bool>              _declutter;
     };
diff --git a/src/osgEarthSymbology/TextSymbol.cpp b/src/osgEarthSymbology/TextSymbol.cpp
index c29eac1..08f0834 100644
--- a/src/osgEarthSymbology/TextSymbol.cpp
+++ b/src/osgEarthSymbology/TextSymbol.cpp
@@ -25,6 +25,7 @@ TextSymbol::TextSymbol( const Config& conf ) :
 Symbol                ( conf ),
 _fill                 ( Fill( 1, 1, 1, 1 ) ),
 _halo                 ( Stroke( 0.3, 0.3, 0.3, 1) ),
+_haloOffset           ( 0.07f ),
 _size                 ( 16.0f ),
 _removeDuplicateLabels( false ),
 _alignment            ( ALIGN_BASE_LINE ),
@@ -42,6 +43,7 @@ TextSymbol::getConfig() const
     conf.key() = "text";
     conf.addObjIfSet( "fill", _fill );
     conf.addObjIfSet( "halo", _halo );
+    conf.addIfSet( "halo_offset", _haloOffset );
     conf.addIfSet( "font", _font );
     conf.addIfSet( "size", _size );
     conf.addObjIfSet( "content", _content );
@@ -53,16 +55,6 @@ TextSymbol::getConfig() const
     conf.addIfSet( "encoding", "utf16", _encoding, ENCODING_UTF16 );
     conf.addIfSet( "encoding", "utf32", _encoding, ENCODING_UTF32 );
 
-#if 0
-    conf.addIfSet( "halign", "left",   _halign, HALIGN_LEFT );
-    conf.addIfSet( "halign", "center", _halign, HALIGN_CENTER );
-    conf.addIfSet( "halign", "right",  _halign, HALIGN_RIGHT );
-
-    conf.addIfSet( "valign", "top",     _valign, VALIGN_TOP );
-    conf.addIfSet( "valign", "center",  _valign, VALIGN_CENTER );
-    conf.addIfSet( "valign", "bottom",  _valign, VALIGN_BOTTOM );
-#endif
-
     conf.addIfSet( "alignment", "left_top",                _alignment, ALIGN_LEFT_TOP );
     conf.addIfSet( "alignment", "left_center",             _alignment, ALIGN_LEFT_CENTER );
     conf.addIfSet( "alignment", "left_bottom",             _alignment, ALIGN_LEFT_BOTTOM );
@@ -80,18 +72,6 @@ TextSymbol::getConfig() const
     conf.addIfSet( "alignment", "right_bottom_base_line",  _alignment, ALIGN_RIGHT_BOTTOM_BASE_LINE );
     conf.addIfSet( "alignment", "base_line",               _alignment, ALIGN_BASE_LINE );
 
-#if 0
-    conf.addIfSet( "rotate_to_screen", _rotateToScreen );
-    conf.addIfSet( "size_mode", "screen", _sizeMode, SIZEMODE_SCREEN );
-    conf.addIfSet( "size_mode", "object", _sizeMode, SIZEMODE_OBJECT );
-    conf.addIfSet( "line_orientation", "parallel", _lineOrientation, LINEORIENTATION_PARALLEL );
-    conf.addIfSet( "line_orientation", "perpendicular", _lineOrientation, LINEORIENTATION_PERPENDICULAR );
-    conf.addIfSet( "line_orientation", "horizontal", _lineOrientation, LINEORIENTATION_HORIZONTAL );
-    conf.addIfSet( "line_placement", "along_line", _linePlacement, LINEPLACEMENT_ALONG_LINE );
-    conf.addIfSet( "line_placement", "centroid", _linePlacement, LINEPLACEMENT_CENTROID );
-    conf.addIfSet( "theme", _theme );
-#endif
-
     conf.addIfSet( "declutter", _declutter );
 
     conf.addIfSet( "provider", _provider );
@@ -107,6 +87,7 @@ TextSymbol::mergeConfig( const Config& conf )
 {
     conf.getObjIfSet( "fill", _fill );
     conf.getObjIfSet( "halo", _halo );
+    conf.getIfSet( "halo_offset", _haloOffset );
     conf.getIfSet( "font", _font );
     conf.getIfSet( "size", _size );
     conf.getObjIfSet( "content", _content );
@@ -135,18 +116,6 @@ TextSymbol::mergeConfig( const Config& conf )
     conf.getIfSet( "alignment", "right_bottom_base_line",  _alignment, ALIGN_RIGHT_BOTTOM_BASE_LINE );
     conf.getIfSet( "alignment", "base_line" ,              _alignment, ALIGN_BASE_LINE );
 
-#if 0
-    conf.getIfSet( "rotate_to_screen", _rotateToScreen );
-    conf.getIfSet( "size_mode", "screen", _sizeMode, SIZEMODE_SCREEN );
-    conf.getIfSet( "size_mode", "object", _sizeMode, SIZEMODE_OBJECT );
-    conf.getIfSet( "line_orientation", "parallel", _lineOrientation, LINEORIENTATION_PARALLEL );
-    conf.getIfSet( "line_orientation", "perpendicular", _lineOrientation, LINEORIENTATION_PERPENDICULAR );
-    conf.getIfSet( "line_orientation", "horizontal", _lineOrientation, LINEORIENTATION_HORIZONTAL );
-    conf.getIfSet( "line_placement", "along_line", _linePlacement, LINEPLACEMENT_ALONG_LINE );
-    conf.getIfSet( "line_placement", "centroid", _linePlacement, LINEPLACEMENT_CENTROID );
-    conf.getIfSet( "theme", _theme );
-#endif
-
     conf.getIfSet( "declutter", _declutter );
 
     conf.getIfSet( "provider", _provider );
diff --git a/src/osgEarthUtil/BrightnessContrastColorFilter.cpp b/src/osgEarthUtil/BrightnessContrastColorFilter.cpp
index 5ff497b..fa8ef57 100644
--- a/src/osgEarthUtil/BrightnessContrastColorFilter.cpp
+++ b/src/osgEarthUtil/BrightnessContrastColorFilter.cpp
@@ -19,7 +19,7 @@
 * Original author: Thomas Lerman
 */
 #include <osgEarthUtil/BrightnessContrastColorFilter>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Program>
diff --git a/src/osgEarthUtil/CMYKColorFilter.cpp b/src/osgEarthUtil/CMYKColorFilter.cpp
index 28c3d98..c7f871c 100644
--- a/src/osgEarthUtil/CMYKColorFilter.cpp
+++ b/src/osgEarthUtil/CMYKColorFilter.cpp
@@ -19,7 +19,7 @@
 * Original author: Thomas Lerman
 */
 #include <osgEarthUtil/CMYKColorFilter>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Program>
diff --git a/src/osgEarthUtil/ChromaKeyColorFilter.cpp b/src/osgEarthUtil/ChromaKeyColorFilter.cpp
index 3b32f8e..b088a66 100644
--- a/src/osgEarthUtil/ChromaKeyColorFilter.cpp
+++ b/src/osgEarthUtil/ChromaKeyColorFilter.cpp
@@ -17,7 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthUtil/ChromaKeyColorFilter>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Program>
@@ -151,7 +151,7 @@ Config
 ChromaKeyColorFilter::getConfig() const
 {
     osg::Vec3f val = getColor();
-    Config conf("rgb");
+    Config conf("chroma_key");
     conf.add( "r", val[0] );
     conf.add( "g", val[1] );
     conf.add( "b", val[2] );
diff --git a/src/osgEarthUtil/Controls b/src/osgEarthUtil/Controls
index 89f9366..1c3482b 100644
--- a/src/osgEarthUtil/Controls
+++ b/src/osgEarthUtil/Controls
@@ -828,7 +828,8 @@ namespace osgEarth { namespace Util { namespace Controls
         bool            _contextDirty;
         bool            _updatePending;
 
-		std::map<osgGA::GUIEventHandler*, osgViewer::View*> _eventHandlersMap;
+        typedef std::map< osg::observer_ptr<osgGA::GUIEventHandler>, osg::observer_ptr<osgViewer::View> > EventHandlersMap;
+		EventHandlersMap _eventHandlersMap;
 
         osg::ref_ptr<ControlNodeBin> _controlNodeBin;
 
diff --git a/src/osgEarthUtil/Controls.cpp b/src/osgEarthUtil/Controls.cpp
index 1d83b0d..80c3195 100644
--- a/src/osgEarthUtil/Controls.cpp
+++ b/src/osgEarthUtil/Controls.cpp
@@ -1090,17 +1090,17 @@ HSliderControl::draw( const ControlContext& cx, DrawableList& out )
             g->setVertexArray( verts );
 
             (*verts)[0].set( rx, vph - ry, 0 );
-            (*verts)[1].set( rx + rw, vph - ry, 0 );
+            (*verts)[1].set( rx, vph - (ry + rh), 0 );
             (*verts)[2].set( rx + rw, vph - (ry + rh), 0 );
-            (*verts)[3].set( rx, vph - (ry + rh), 0 );
+            (*verts)[3].set( rx + rw, vph - ry, 0 );
             g->addPrimitiveSet( new osg::DrawArrays( GL_LINE_LOOP, 0, 4 ) );
 
             float hx = rx + rw * ( (_value-_min)/(_max-_min) );
 
             (*verts)[4].set( hx-4, vph - ry + 3, 0 );
-            (*verts)[5].set( hx+4, vph - ry + 3, 0 );
+            (*verts)[5].set( hx-4, vph - (ry + rh + 3), 0 );
             (*verts)[6].set( hx+4, vph - (ry + rh + 3), 0 );
-            (*verts)[7].set( hx-4, vph - (ry + rh + 3), 0 );
+            (*verts)[7].set( hx+4, vph - ry + 3, 0 );
             g->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 4, 4 ) );
 
             osg::Vec4Array* c = new osg::Vec4Array(1);
@@ -2497,14 +2497,16 @@ ControlCanvas::~ControlCanvas()
     OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _viewCanvasMapMutex );
     _viewCanvasMap.erase( _context._view );
 
-    // commented out: causing a crash on exit
-#if 0
-    std::map<osgGA::GUIEventHandler*, osgViewer::View*>::iterator itr;
+    EventHandlersMap::iterator itr;
     for (itr = _eventHandlersMap.begin(); itr != _eventHandlersMap.end(); ++itr)
     {
-        itr->second->removeEventHandler(itr->first);
+		osgGA::GUIEventHandler* pGUIEventHandler = itr->first.get();
+		osgViewer::View* pView = itr->second.get();
+		if ( (pView != NULL) && (pGUIEventHandler != NULL) )
+		{
+			pView->removeEventHandler(pGUIEventHandler);
+		}
     }
-#endif
 }
 
 void
diff --git a/src/osgEarthUtil/EarthManipulator b/src/osgEarthUtil/EarthManipulator
index df50c9c..0b7b92e 100644
--- a/src/osgEarthUtil/EarthManipulator
+++ b/src/osgEarthUtil/EarthManipulator
@@ -24,9 +24,9 @@
 #include <osgEarth/Viewpoint>
 #include <osgEarth/GeoData>
 #include <osgEarth/Revisioning>
+#include <osgEarth/Terrain>
 #include <osg/Timer>
 #include <osgGA/CameraManipulator>
-#include <osgEarth/Terrain>
 #include <map>
 #include <list>
 #include <utility>
diff --git a/src/osgEarthUtil/EarthManipulator.cpp b/src/osgEarthUtil/EarthManipulator.cpp
index 3a719c3..92239e7 100644
--- a/src/osgEarthUtil/EarthManipulator.cpp
+++ b/src/osgEarthUtil/EarthManipulator.cpp
@@ -2017,6 +2017,42 @@ EarthManipulator::zoom( double dx, double dy )
 }
 
 
+namespace
+{
+    // osg::View::getCameraContainingPosition has a bug in it. If the camera's current event
+    // state is not up to date (after a window resize, for example), it still uses that event
+    // state to get the window's current size instead of using the Viewport.
+    //
+    // This version works around that
+
+    const osg::Camera*
+    getCameraContainingPosition(osgViewer::View* view, float x, float y, float& out_local_x, float& out_local_y)
+    {
+        osg::Camera* camera = view->getCamera();
+        osg::Viewport* viewport = camera->getViewport();
+
+        if ( camera->getGraphicsContext() && viewport )
+        {
+            double new_x = x;
+            double new_y = y;
+            
+            const double epsilon = 0.5;
+
+            if (
+                new_x >= (viewport->x()-epsilon) && new_y >= (viewport->y()-epsilon) &&
+                new_x < (viewport->x()+viewport->width()-1.0+epsilon) && new_y <= (viewport->y()+viewport->height()-1.0+epsilon) )
+            {
+                out_local_x = new_x;
+                out_local_y = new_y;
+                return camera;
+            }
+        }
+
+        return view->getCameraContainingPosition(x, y, out_local_x, out_local_y);
+    }
+}
+
+
 bool
 EarthManipulator::screenToWorld(float x, float y, osg::View* theView, osg::Vec3d& out_coords ) const
 {
@@ -2030,7 +2066,7 @@ EarthManipulator::screenToWorld(float x, float y, osg::View* theView, osg::Vec3d
         return false;
 
     float local_x, local_y = 0.0;
-    const osg::Camera* camera = view->getCameraContainingPosition(x, y, local_x, local_y);
+    const osg::Camera* camera = getCameraContainingPosition(view, x, y, local_x, local_y);
     if ( !camera )
         return false;
 
@@ -2157,6 +2193,9 @@ EarthManipulator::handlePointAction( const Action& action, float mx, float my, o
 
                 osg::Vec3d pointVP;
                 here.getSRS()->transformFromWorld(point, pointVP);
+
+                //OE_NOTICE << "X=" << pointVP.x() << ", Y=" << pointVP.y() << std::endl;
+
                 here.setFocalPoint( pointVP );
 
                 double duration_s = action.getDoubleOption(OPTION_DURATION, 1.0);
diff --git a/src/osgEarthUtil/ElevationManager b/src/osgEarthUtil/ElevationManager
index 6f2eeaa..4829f99 100644
--- a/src/osgEarthUtil/ElevationManager
+++ b/src/osgEarthUtil/ElevationManager
@@ -20,8 +20,7 @@
 #define OSGEARTHUTIL_ELEVATION_MANAGER_H
 
 #include <osgEarthUtil/Common>
-#include <osgEarth/Map>
-#include <osgEarth/MapNode>
+#include <osgEarth/MapFrame>
 
 namespace osgEarth { namespace Util
 {
diff --git a/src/osgEarthUtil/ExampleResources.cpp b/src/osgEarthUtil/ExampleResources.cpp
index ea3028a..f551831 100644
--- a/src/osgEarthUtil/ExampleResources.cpp
+++ b/src/osgEarthUtil/ExampleResources.cpp
@@ -381,6 +381,7 @@ AnnotationGraphControlFactory::create(osg::Node*       graph,
                                       osgViewer::View* view) const
 {
     AnnoControlBuilder builder( view );
+	builder.setNodeMaskOverride(~0);
     if ( graph )
         graph->accept( builder );
 
@@ -456,6 +457,7 @@ MapNodeHelper::parse(MapNode*             mapNode,
                      osg::Group*          root,
                      Control*             userControl ) const
 {
+    // this is a dubious move.
     if ( !root )
         root = mapNode;
 
@@ -629,7 +631,17 @@ MapNodeHelper::parse(MapNode*             mapNode,
     // Install an auto clip plane clamper
     if ( useAutoClip )
     {
-        view->getCamera()->addCullCallback( new AutoClipPlaneCullCallback(mapNode) );
+#if 0
+        HorizonClipNode* hcn = new HorizonClipNode( mapNode );
+        if ( mapNode->getNumParents() == 1 )
+        {
+            osg::Group* parent = mapNode->getParent(0);
+            hcn->addChild( mapNode );
+            parent->replaceChild( mapNode, hcn );
+        }
+#else
+        mapNode->addCullCallback( new AutoClipPlaneCullCallback(mapNode) );
+#endif
     }
 
     root->addChild( canvas );
diff --git a/src/osgEarthUtil/GammaColorFilter.cpp b/src/osgEarthUtil/GammaColorFilter.cpp
index 13d8477..1f53a58 100644
--- a/src/osgEarthUtil/GammaColorFilter.cpp
+++ b/src/osgEarthUtil/GammaColorFilter.cpp
@@ -19,7 +19,7 @@
 * Original author: Thomas Lerman
 */
 #include <osgEarthUtil/GammaColorFilter>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Program>
@@ -147,4 +147,4 @@ GammaColorFilter::getConfig() const
         conf.add("b", rgb[2]);
     }
     return conf;
-}
\ No newline at end of file
+}
diff --git a/src/osgEarthUtil/HSLColorFilter.cpp b/src/osgEarthUtil/HSLColorFilter.cpp
index a784922..7efd4a2 100644
--- a/src/osgEarthUtil/HSLColorFilter.cpp
+++ b/src/osgEarthUtil/HSLColorFilter.cpp
@@ -17,7 +17,7 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include <osgEarthUtil/HSLColorFilter>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Program>
diff --git a/src/osgEarthUtil/RGBColorFilter.cpp b/src/osgEarthUtil/RGBColorFilter.cpp
index e002321..cc53e3f 100644
--- a/src/osgEarthUtil/RGBColorFilter.cpp
+++ b/src/osgEarthUtil/RGBColorFilter.cpp
@@ -19,7 +19,7 @@
 * Original author: Thomas Lerman
 */
 #include <osgEarthUtil/RGBColorFilter>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Program>
diff --git a/src/osgEarthUtil/ShadowUtils.cpp b/src/osgEarthUtil/ShadowUtils.cpp
index ae5d3d2..0dbfcfa 100644
--- a/src/osgEarthUtil/ShadowUtils.cpp
+++ b/src/osgEarthUtil/ShadowUtils.cpp
@@ -21,7 +21,7 @@
 #include <osgEarth/MapNode>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Notify>
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarth/TextureCompositor>
 #include <osgEarth/Registry>
@@ -128,37 +128,39 @@ ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
 
     std::stringstream buf;
     buf << "#version " << GLSL_VERSION_STR << "\n";
-    buf << "varying vec4 colorAmbientEmissive;\n";
-    buf << "varying vec4 shadow_TexCoord0;\n";
+#ifdef OSG_GLES2_AVAILABLE
+    buf << "precision mediump float;\n";
+#endif
+    buf << "varying vec4 oe_shadow_ambient;\n";
+    buf << "varying vec4 oe_shadow_TexCoord0;\n";
     if ( su1 >= 0 )
-        buf << "varying vec4 shadow_TexCoord1;\n";
+        buf << "varying vec4 oe_shadow_TexCoord1;\n";
 
 
-    buf << "void osgearth_vert_setupShadowCoords()\n";
+    buf << "void oe_shadow_setupShadowCoords()\n";
     buf << "{\n";
     buf << "    vec4 position4 = gl_ModelViewMatrix * gl_Vertex;\n";
-    buf << "    shadow_TexCoord0.s = dot( position4, gl_EyePlaneS[" << su <<"]);\n";
-    buf << "    shadow_TexCoord0.t = dot( position4, gl_EyePlaneT[" << su <<"]);\n";
-    buf << "    shadow_TexCoord0.p = dot( position4, gl_EyePlaneR[" << su <<"]);\n";
-    buf << "    shadow_TexCoord0.q = dot( position4, gl_EyePlaneQ[" << su <<"]);\n";
+    buf << "    oe_shadow_TexCoord0.s = dot( position4, gl_EyePlaneS[" << su <<"]);\n";
+    buf << "    oe_shadow_TexCoord0.t = dot( position4, gl_EyePlaneT[" << su <<"]);\n";
+    buf << "    oe_shadow_TexCoord0.p = dot( position4, gl_EyePlaneR[" << su <<"]);\n";
+    buf << "    oe_shadow_TexCoord0.q = dot( position4, gl_EyePlaneQ[" << su <<"]);\n";
     if (su1 >= 0)
     {
-        buf << "    shadow_TexCoord1.s = dot( position4, gl_EyePlaneS[" << su1 <<"]);\n";
-        buf << "    shadow_TexCoord1.t = dot( position4, gl_EyePlaneT[" << su1 <<"]);\n";
-        buf << "    shadow_TexCoord1.p = dot( position4, gl_EyePlaneR[" << su1 <<"]);\n";
-        buf << "    shadow_TexCoord1.q = dot( position4, gl_EyePlaneQ[" << su1 <<"]);\n";
+        buf << "    oe_shadow_TexCoord1.s = dot( position4, gl_EyePlaneS[" << su1 <<"]);\n";
+        buf << "    oe_shadow_TexCoord1.t = dot( position4, gl_EyePlaneT[" << su1 <<"]);\n";
+        buf << "    oe_shadow_TexCoord1.p = dot( position4, gl_EyePlaneR[" << su1 <<"]);\n";
+        buf << "    oe_shadow_TexCoord1.q = dot( position4, gl_EyePlaneQ[" << su1 <<"]);\n";
     }
-    buf << "    colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor\n";
-    buf << "                         + gl_FrontLightProduct[0].ambient;\n";
-    //buf << "    colorAmbientEmissive = gl_LightModel.ambient + gl_FrontLightProduct[0].ambient; \n";
-    //buf << "    colorAmbientEmissive = gl_FrontLightProduct[0].ambient; \n";
-    buf << "}\n";
+
+    // the ambient lighting will control the intensity of the shadow.
+    buf << "    oe_shadow_ambient = gl_FrontLightProduct[0].ambient; \n"
+        << "}\n";
 
     std::string setupShadowCoords;
     setupShadowCoords = buf.str();
 
     vp->setFunction(
-        "osgearth_vert_setupShadowCoords", 
+        "oe_shadow_setupShadowCoords", 
         setupShadowCoords, 
         ShaderComp::LOCATION_VERTEX_POST_LIGHTING,
         -1.0 );
@@ -166,40 +168,46 @@ ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
     std::stringstream buf2;
     buf2 <<
         "#version " << GLSL_VERSION_STR << "\n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float;\n"
+#endif
         "uniform sampler2DShadow shadowTexture;\n"
-        "varying vec4 shadow_TexCoord0;\n";
+        "varying vec4 oe_shadow_TexCoord0;\n";
 
     if (su1 >= 0)
     {
         // bound by vdsm
         buf2 << "uniform sampler2DShadow shadowTexture1;\n";
-        buf2 << "varying vec4 shadow_TexCoord1;\n";
+        buf2 << "varying vec4 oe_shadow_TexCoord1;\n";
     }
     buf2 <<
-        "varying vec4 colorAmbientEmissive;\n"
-        "varying vec4 osg_FrontColor;\n"
-        "varying vec4 osg_FrontSecondaryColor;\n"
-        "void osgearth_frag_applyLighting( inout vec4 color )\n"
+        "varying vec4 oe_shadow_ambient;\n"
+
+        "void oe_shadow_applyLighting( inout vec4 color )\n"
         "{\n"
         "    float alpha = color.a;\n"
-        "    float shadowFac = shadow2DProj( shadowTexture, shadow_TexCoord0).r;\n";
+        "    float shadowFac = shadow2DProj( shadowTexture, oe_shadow_TexCoord0).r;\n";
     if (su1 > 0)
     {
-        buf2 << "    shadowFac *= shadow2DProj( shadowTexture1, shadow_TexCoord1).r;\n";
+        buf2 << "    shadowFac *= shadow2DProj( shadowTexture1, oe_shadow_TexCoord1).r;\n";
     }
+
+    // calculate the shadowed color and mix if with the lit color based on the
+    // ambient lighting. The 0.5 is a multiplier that darkens the shadow in
+    // proportion to ambient light. It should probably be a uniform.
     buf2 <<
-        "    vec4 diffuseLight = mix(colorAmbientEmissive, osg_FrontColor, shadowFac);\n"
-        "    color = color * diffuseLight + osg_FrontSecondaryColor * shadowFac;\n"
+        "    vec4 colorInFullShadow = color * oe_shadow_ambient; \n"
+        "    color = mix(colorInFullShadow, color, shadowFac); \n"
         "    color.a = alpha;\n"
         "}\n";
 
     std::string fragApplyLighting;
     fragApplyLighting = buf2.str();
 
-    // override the terrain engine's default lighting shader:
-    vp->setShader("osgearth_frag_applyLighting",
-        new osg::Shader(osg::Shader::FRAGMENT, fragApplyLighting),
-        osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
+    vp->setFunction(
+        "oe_shadow_applyLighting",
+        fragApplyLighting,
+        osgEarth::ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING );
 
     setShadowUnit(sscene, su);
 
diff --git a/src/osgEarthUtil/SkyNode.cpp b/src/osgEarthUtil/SkyNode.cpp
index d93359f..37b450d 100644
--- a/src/osgEarthUtil/SkyNode.cpp
+++ b/src/osgEarthUtil/SkyNode.cpp
@@ -19,7 +19,7 @@
 #include <osgEarthUtil/SkyNode>
 #include <osgEarthUtil/StarData>
 
-#include <osgEarth/ShaderComposition>
+#include <osgEarth/VirtualProgram>
 #include <osgEarthUtil/LatLongFormatter>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/MapNode>
@@ -788,12 +788,45 @@ SkyNode::traverse( osg::NodeVisitor& nv )
     osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
     if ( cv )
     {
+        // If there's a custom projection matrix clamper installed, remove it temporarily.
+        // We dont' want it mucking with our sky elements.
+        osg::ref_ptr<osg::CullSettings::ClampProjectionMatrixCallback> cb = cv->getClampProjectionMatrixCallback();
+        cv->setClampProjectionMatrixCallback( 0L );
+
         osg::View* view = cv->getCurrentCamera()->getView();
         PerViewDataMap::iterator i = _perViewData.find( view );
         if ( i != _perViewData.end() )
         {
+#if 0
+            // adjust the light color based on the eye point and the sun position.
+            float aMin =  0.1f;
+            float aMax =  0.9f;
+            float dMin = -0.5f;
+            float dMax =  0.5f;
+
+            osg::Vec3 eye = cv->getViewPoint();
+            eye.normalize();
+
+            osg::Vec3 sun = i->second._lightPos;
+            sun.normalize();
+
+            // clamp to valid range:
+            float d = osg::clampBetween(eye * sun, dMin, dMax);
+
+            // remap to [0..1]:
+            d = (d-dMin) / (dMax-dMin);
+
+            // map to ambient level:
+            float diff = aMin + d * (aMax-aMin);
+
+            i->second._light->setDiffuse( osg::Vec4(diff,diff,diff,1.0) );
+#endif
+
             i->second._cullContainer->accept( nv );
         }
+
+        // restore a custom clamper.
+        if ( cb.valid() ) cv->setClampProjectionMatrixCallback( cb.get() );
     }
 
     else
@@ -860,7 +893,7 @@ SkyNode::attach( osg::View* view, int lightNum )
     data._starsMatrix = _defaultPerViewData._starsMatrix;
     data._starsXform->setMatrix( _defaultPerViewData._starsMatrix );
     data._starsXform->addChild( _stars.get() );
-    data._cullContainer->addChild( data._starsXform.get() );    
+    data._cullContainer->addChild( data._starsXform.get() );
     data._starsVisible = _defaultPerViewData._starsVisible;
     data._starsXform->setNodeMask( data._starsVisible ? ~0 : 0 );
 
@@ -1154,79 +1187,81 @@ SkyNode::makeAtmosphere( const osg::EllipsoidModel* em )
 
     // configure the state set:
     set->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
-    //set->setMode( GL_CULL_FACE, osg::StateAttribute::ON );
-    set->setMode( GL_CULL_FACE, osg::StateAttribute::OFF );
+    set->setAttributeAndModes( new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON );
     //set->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
     set->setAttributeAndModes( new osg::Depth( osg::Depth::LESS, 0, 1, false ) ); // no depth write
+    set->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false) ); // no zbuffer
     set->setAttributeAndModes( new osg::BlendFunc( GL_ONE, GL_ONE ), osg::StateAttribute::ON );
 
-    // next, create and add the shaders:
-    osg::Program* program = new osg::Program();
-    osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, Stringify()
-        << s_versionString
-        << s_mathUtils
-        << s_atmosphereVertexDeclarations
-        << s_atmosphereVertexShared
-        << s_atmosphereVertexMain );
-    program->addShader( vs );
-
-    osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, Stringify()
-        << s_versionString
+    if ( Registry::capabilities().supportsGLSL() )
+    {
+        // next, create and add the shaders:
+        osg::Program* program = new osg::Program();
+        osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, Stringify()
+            << s_versionString
+            << s_mathUtils
+            << s_atmosphereVertexDeclarations
+            << s_atmosphereVertexShared
+            << s_atmosphereVertexMain );
+        program->addShader( vs );
+
+        osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, Stringify()
+            << s_versionString
 #ifdef OSG_GLES2_AVAILABLE
-        << "precision highp float;\n"
+            << "precision highp float;\n"
 #endif
-        << s_mathUtils
-        << s_atmosphereFragmentDeclarations
-        //<< s_atmosphereFragmentShared
-        << s_atmosphereFragmentMain );
-    program->addShader( fs );
-
-    set->setAttributeAndModes( program, osg::StateAttribute::ON );
-
-    // apply the uniforms:
-    float r_wl   = ::powf( .65f, 4.0f );
-    float g_wl = ::powf( .57f, 4.0f );
-    float b_wl  = ::powf( .475f, 4.0f );
-    osg::Vec3 RGB_wl( 1.0f/r_wl, 1.0f/g_wl, 1.0f/b_wl );
-    float Kr = 0.0025f;
-    float Kr4PI = Kr * 4.0f * osg::PI;
-    float Km = 0.0015f;
-    float Km4PI = Km * 4.0f * osg::PI;
-    float ESun = 15.0f;
-    float MPhase = -.095f;
-    float RayleighScaleDepth = 0.25f;
-    int   Samples = 2;
-    float Weather = 1.0f;
-    
-    float Scale = 1.0f / (_outerRadius - _innerRadius);
-
-    // TODO: replace this with a UBO.
-
-    set->getOrCreateUniform( "atmos_v3InvWavelength", osg::Uniform::FLOAT_VEC3 )->set( RGB_wl );
-    set->getOrCreateUniform( "atmos_fInnerRadius",    osg::Uniform::FLOAT )->set( _innerRadius );
-    set->getOrCreateUniform( "atmos_fInnerRadius2",   osg::Uniform::FLOAT )->set( _innerRadius * _innerRadius );
-    set->getOrCreateUniform( "atmos_fOuterRadius",    osg::Uniform::FLOAT )->set( _outerRadius );
-    set->getOrCreateUniform( "atmos_fOuterRadius2",   osg::Uniform::FLOAT )->set( _outerRadius * _outerRadius );
-    set->getOrCreateUniform( "atmos_fKrESun",         osg::Uniform::FLOAT )->set( Kr * ESun );
-    set->getOrCreateUniform( "atmos_fKmESun",         osg::Uniform::FLOAT )->set( Km * ESun );
-    set->getOrCreateUniform( "atmos_fKr4PI",          osg::Uniform::FLOAT )->set( Kr4PI );
-    set->getOrCreateUniform( "atmos_fKm4PI",          osg::Uniform::FLOAT )->set( Km4PI );
-    set->getOrCreateUniform( "atmos_fScale",          osg::Uniform::FLOAT )->set( Scale );
-    set->getOrCreateUniform( "atmos_fScaleDepth",     osg::Uniform::FLOAT )->set( RayleighScaleDepth );
-    set->getOrCreateUniform( "atmos_fScaleOverScaleDepth", osg::Uniform::FLOAT )->set( Scale / RayleighScaleDepth );
-    set->getOrCreateUniform( "atmos_g",               osg::Uniform::FLOAT )->set( MPhase );
-    set->getOrCreateUniform( "atmos_g2",              osg::Uniform::FLOAT )->set( MPhase * MPhase );
-    set->getOrCreateUniform( "atmos_nSamples",        osg::Uniform::INT )->set( Samples );
-    set->getOrCreateUniform( "atmos_fSamples",        osg::Uniform::FLOAT )->set( (float)Samples );
-    set->getOrCreateUniform( "atmos_fWeather",        osg::Uniform::FLOAT )->set( Weather );
+            << s_mathUtils
+            << s_atmosphereFragmentDeclarations
+            //<< s_atmosphereFragmentShared
+            << s_atmosphereFragmentMain );
+        program->addShader( fs );
+
+        set->setAttributeAndModes( program, osg::StateAttribute::ON );
+
+        // apply the uniforms:
+        float r_wl   = ::powf( .65f, 4.0f );
+        float g_wl = ::powf( .57f, 4.0f );
+        float b_wl  = ::powf( .475f, 4.0f );
+        osg::Vec3 RGB_wl( 1.0f/r_wl, 1.0f/g_wl, 1.0f/b_wl );
+        float Kr = 0.0025f;
+        float Kr4PI = Kr * 4.0f * osg::PI;
+        float Km = 0.0015f;
+        float Km4PI = Km * 4.0f * osg::PI;
+        float ESun = 15.0f;
+        float MPhase = -.095f;
+        float RayleighScaleDepth = 0.25f;
+        int   Samples = 2;
+        float Weather = 1.0f;
+
+        float Scale = 1.0f / (_outerRadius - _innerRadius);
+
+        // TODO: replace this with a UBO.
+
+        set->getOrCreateUniform( "atmos_v3InvWavelength", osg::Uniform::FLOAT_VEC3 )->set( RGB_wl );
+        set->getOrCreateUniform( "atmos_fInnerRadius",    osg::Uniform::FLOAT )->set( _innerRadius );
+        set->getOrCreateUniform( "atmos_fInnerRadius2",   osg::Uniform::FLOAT )->set( _innerRadius * _innerRadius );
+        set->getOrCreateUniform( "atmos_fOuterRadius",    osg::Uniform::FLOAT )->set( _outerRadius );
+        set->getOrCreateUniform( "atmos_fOuterRadius2",   osg::Uniform::FLOAT )->set( _outerRadius * _outerRadius );
+        set->getOrCreateUniform( "atmos_fKrESun",         osg::Uniform::FLOAT )->set( Kr * ESun );
+        set->getOrCreateUniform( "atmos_fKmESun",         osg::Uniform::FLOAT )->set( Km * ESun );
+        set->getOrCreateUniform( "atmos_fKr4PI",          osg::Uniform::FLOAT )->set( Kr4PI );
+        set->getOrCreateUniform( "atmos_fKm4PI",          osg::Uniform::FLOAT )->set( Km4PI );
+        set->getOrCreateUniform( "atmos_fScale",          osg::Uniform::FLOAT )->set( Scale );
+        set->getOrCreateUniform( "atmos_fScaleDepth",     osg::Uniform::FLOAT )->set( RayleighScaleDepth );
+        set->getOrCreateUniform( "atmos_fScaleOverScaleDepth", osg::Uniform::FLOAT )->set( Scale / RayleighScaleDepth );
+        set->getOrCreateUniform( "atmos_g",               osg::Uniform::FLOAT )->set( MPhase );
+        set->getOrCreateUniform( "atmos_g2",              osg::Uniform::FLOAT )->set( MPhase * MPhase );
+        set->getOrCreateUniform( "atmos_nSamples",        osg::Uniform::INT )->set( Samples );
+        set->getOrCreateUniform( "atmos_fSamples",        osg::Uniform::FLOAT )->set( (float)Samples );
+        set->getOrCreateUniform( "atmos_fWeather",        osg::Uniform::FLOAT )->set( Weather );
+    }
     
     // A nested camera isolates the projection matrix calculations so the node won't 
     // affect the clip planes in the rest of the scene.
     osg::Camera* cam = new osg::Camera();
     cam->getOrCreateStateSet()->setRenderBinDetails( BIN_ATMOSPHERE, "RenderBin" );
     cam->setRenderOrder( osg::Camera::NESTED_RENDER );
-    //cam->setComputeNearFarMode( osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES );
-    cam->setComputeNearFarMode( osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES );
+    cam->setComputeNearFarMode( osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES );
     cam->addChild( geode );
 
     _atmosphere = cam;
@@ -1255,20 +1290,23 @@ SkyNode::makeSun()
    // set->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON );
 
     // create shaders
-    osg::Program* program = new osg::Program();
-    osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, Stringify()
-        << s_versionString
-        << s_sunVertexSource );
-    program->addShader( vs );
-    osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, Stringify()
-        << s_versionString
+    if ( Registry::capabilities().supportsGLSL() )
+    {
+        osg::Program* program = new osg::Program();
+        osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, Stringify()
+            << s_versionString
+            << s_sunVertexSource );
+        program->addShader( vs );
+        osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, Stringify()
+            << s_versionString
 #ifdef OSG_GLES2_AVAILABLE
-        << "precision highp float;\n"
+            << "precision highp float;\n"
 #endif
-        << s_mathUtils
-        << s_sunFragmentSource );
-    program->addShader( fs );
-    set->setAttributeAndModes( program, osg::StateAttribute::ON );
+            << s_mathUtils
+            << s_sunFragmentSource );
+        program->addShader( fs );
+        set->setAttributeAndModes( program, osg::StateAttribute::ON );
+    }
 
     // make the sun's transform:
     // todo: move this?
@@ -1322,21 +1360,24 @@ SkyNode::makeMoon()
 
 #ifdef OSG_GLES2_AVAILABLE
     
-    set->addUniform(new osg::Uniform("moonTex", 0));
-    
-    // create shaders
-    osg::Program* program = new osg::Program();
-    osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, Stringify()
-                                      << s_versionString
-                                      << "precision highp float;\n"
-                                      << s_moonVertexSource );
-    program->addShader( vs );
-    osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, Stringify()
-                                      << s_versionString
-                                      << "precision highp float;\n"
-                                      << s_moonFragmentSource );
-    program->addShader( fs );
-    set->setAttributeAndModes( program, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+    if ( Registry::capabilities().supportsGLSL() )
+    {
+        set->addUniform(new osg::Uniform("moonTex", 0));
+        
+        // create shaders
+        osg::Program* program = new osg::Program();
+        osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, Stringify()
+                                          << s_versionString
+                                          << "precision highp float;\n"
+                                          << s_moonVertexSource );
+        program->addShader( vs );
+        osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, Stringify()
+                                          << s_versionString
+                                          << "precision highp float;\n"
+                                          << s_moonFragmentSource );
+        program->addShader( fs );
+        set->setAttributeAndModes( program, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+    }
 #endif
     
     // make the moon's transform:
@@ -1397,9 +1438,6 @@ SkyNode::makeStars(const std::string& starFile)
 
   osg::Node* starNode = buildStarGeometry(stars);
 
-  //AddCallbackToDrawablesVisitor visitor(_starRadius);
-  //starNode->accept(visitor);
-
   _stars = starNode;
 }
 
@@ -1437,13 +1475,16 @@ SkyNode::buildStarGeometry(const std::vector<StarData>& stars)
 
   osg::StateSet* sset = geometry->getOrCreateStateSet();
 
-  sset->setTextureAttributeAndModes( 0, new osg::PointSprite(), osg::StateAttribute::ON );
-  sset->setMode( GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON );
+  if ( Registry::capabilities().supportsGLSL() )
+  {
+    sset->setTextureAttributeAndModes( 0, new osg::PointSprite(), osg::StateAttribute::ON );
+    sset->setMode( GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON );
 
-  osg::Program* program = new osg::Program;
-  program->addShader( new osg::Shader(osg::Shader::VERTEX, s_createStarVertexSource()) );
-  program->addShader( new osg::Shader(osg::Shader::FRAGMENT, s_createStarFragmentSource()) );
-  sset->setAttributeAndModes( program, osg::StateAttribute::ON );
+    osg::Program* program = new osg::Program;
+    program->addShader( new osg::Shader(osg::Shader::VERTEX, s_createStarVertexSource()) );
+    program->addShader( new osg::Shader(osg::Shader::FRAGMENT, s_createStarFragmentSource()) );
+    sset->setAttributeAndModes( program, osg::StateAttribute::ON );
+  }
 
   sset->setRenderBinDetails( BIN_STARS, "RenderBin");
   sset->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), osg::StateAttribute::ON );
diff --git a/src/osgEarthUtil/SpatialData.cpp b/src/osgEarthUtil/SpatialData.cpp
index 20a7067..6a45dd6 100644
--- a/src/osgEarthUtil/SpatialData.cpp
+++ b/src/osgEarthUtil/SpatialData.cpp
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthUtil/SpatialData>
+#include <osgEarth/Registry>
 #include <osgUtil/CullVisitor>
 #include <osg/PolygonOffset>
 #include <osg/Polytope>
@@ -44,8 +45,7 @@ namespace
 
         return row*xdim + col;
     }
-
-    static osgText::Font* s_font = 0L;
+    
 
     osg::Geode* makeClusterGeode( const GeoExtent& cellExtent, unsigned num )
     {
@@ -66,10 +66,8 @@ namespace
         t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS );
         t->setCharacterSize( 22.0f );
         t->setAutoRotateToScreen( true );
-
-        if ( !s_font )
-            s_font = osgText::readFontFile( "arialbd.ttf" );
-        t->setFont( s_font );
+        
+        t->setFont( osgEarth::Registry::instance()->getDefaultFont() );
 
         t->setBackdropType( osgText::Text::OUTLINE );
         t->setColor( osg::Vec4(1,1,1,1) );
diff --git a/src/osgEarthUtil/TMSPackager b/src/osgEarthUtil/TMSPackager
index b63de0d..e58ebfc 100644
--- a/src/osgEarthUtil/TMSPackager
+++ b/src/osgEarthUtil/TMSPackager
@@ -39,7 +39,7 @@ namespace osgEarth { namespace Util
          * Constructs a new packager.
          * @param profile    Profile of packaged tile data (required)
          */
-        TMSPackager( const Profile* outProfile );
+        TMSPackager( const Profile* outProfile, osgDB::Options* imageWriteOptions);
 
         /** dtor */
         virtual ~TMSPackager() { }
@@ -153,6 +153,7 @@ namespace osgEarth { namespace Util
         unsigned                    _maxLevel;
         std::vector<GeoExtent>      _extents;
         osg::ref_ptr<const Profile> _outProfile;
+        osg::ref_ptr<osgDB::Options>    _imageWriteOptions;
     };
 
 } } // namespace osgEarth::Util
diff --git a/src/osgEarthUtil/TMSPackager.cpp b/src/osgEarthUtil/TMSPackager.cpp
index bd96dec..2907262 100644
--- a/src/osgEarthUtil/TMSPackager.cpp
+++ b/src/osgEarthUtil/TMSPackager.cpp
@@ -30,14 +30,15 @@ using namespace osgEarth::Util;
 using namespace osgEarth;
 
 
-TMSPackager::TMSPackager(const Profile* outProfile) :
-_outProfile                     ( outProfile ),
-_maxLevel                       ( 5 ),
-_verbose                        ( false ),
-_overwrite                      ( false ),
-_keepEmptyImageTiles            ( false ),
+TMSPackager::TMSPackager(const Profile* outProfile, osgDB::Options* imageWriteOptions) :
+_outProfile         ( outProfile ),
+_maxLevel           ( 99 ),
+_verbose            ( false ),
+_overwrite          ( false ),
+_keepEmptyImageTiles( false ),
 _subdivideSingleColorImageTiles ( false ),
-_abortOnError                   ( true )
+_abortOnError       ( true ),
+_imageWriteOptions  (imageWriteOptions)
 {
     //nop
 }
@@ -126,7 +127,7 @@ TMSPackager::packageImageTile(ImageLayer*          layer,
 
                     // dump it to disk
                     osgDB::makeDirectoryForFile( path );
-                    tileOK = osgDB::writeImageFile( *final.get(), path );
+                    tileOK = osgDB::writeImageFile( *final.get(), path, _imageWriteOptions);
 
                     if ( _verbose )
                     {
diff --git a/tests/annotation.earth b/tests/annotation.earth
index d252338..ae63859 100644
--- a/tests/annotation.earth
+++ b/tests/annotation.earth
@@ -72,6 +72,7 @@ osgEarth Sample - Annotations
                     stroke-width:     3;
                     extrusion-height: 30000;
                 </style>
+                <lighting>false</lighting>
             </feature>
             
             <local_geometry>
@@ -83,6 +84,7 @@ osgEarth Sample - Annotations
                     fill: #00ff00;
                     stroke: #ffff00;
                 </style>
+                <lighting>false</lighting>
             </local_geometry>
             
             <model>
@@ -95,6 +97,7 @@ osgEarth Sample - Annotations
                 <alpha>1.0</alpha>
                 <geometry>POLYGON((-81 26, -80.5 26, -80.5 26.5, -81 26.5))</geometry>
             </imageoverlay>
+            
             <label text="image overlay">
                 <position lat="26" long="-81" />
             </label>
diff --git a/tests/boston.earth b/tests/boston.earth
index c70e0fa..7e71775 100644
--- a/tests/boston.earth
+++ b/tests/boston.earth
@@ -56,6 +56,7 @@ to extruded buildings.
                     extrusion-height:      3.5 * max([story_ht_], 1);
                     extrusion-flatten:     true;
                     extrusion-wall-style:  building-wall;
+                    extrusion-wall-gradient: 0.8;
                     extrusion-roof-style:  building-rooftop;
                     altitude-clamping:     none;
                 }            
@@ -73,9 +74,7 @@ to extruded buildings.
                     fill:             #ffffff;
                 }
             </style>
-        </styles>
-
-        <lighting>true</lighting>        
+        </styles>   
     </model>
     
     <external>
diff --git a/tests/feature_extrude.earth b/tests/feature_extrude.earth
index 8d40d25..871b9a8 100644
--- a/tests/feature_extrude.earth
+++ b/tests/feature_extrude.earth
@@ -24,11 +24,12 @@ Footprint Extrusion using the "feature_geom" driver.
         <styles>
             <style type="text/css">
                 buildings {
-                    extrusion-height:  15;
-                    extrusion-flatten: true;
-                    fill:              #ff7f2f;
-                    altitude-clamping: terrain;
-                    altitude-resolution: 0.001;
+                    fill:                    #ff7f2f;
+                    extrusion-height:        15;
+                    extrusion-flatten:       true;
+                    extrusion-wall-gradient: 0.7;
+                    altitude-clamping:       terrain;
+                    altitude-resolution:     0.001;
                 }            
             </style>
         </styles>   
diff --git a/tests/feature_model_scatter.earth b/tests/feature_model_scatter.earth
index b62cd91..088a137 100644
--- a/tests/feature_model_scatter.earth
+++ b/tests/feature_model_scatter.earth
@@ -13,7 +13,7 @@ is randomized, but it is randomized exactly the same way each time.
     <!-- Our features layer. The "feature_geom" driver will analyze the
          style sheet and determine how to render the feature data. -->
          
-    <model name="trees" driver="feature_geom">
+    <model name="trees" driver="feature_geom" enabled="true">
           
         <!-- Feature data set to load. This is a set of polygons representing
              the public parks in Washington DC -->
@@ -31,6 +31,9 @@ is randomized, but it is randomized exactly the same way each time.
              real features anyway. -->
         <feature_indexing>false</feature_indexing>
         
+        <!-- Fade in new tiles over one second. -->
+         <fade_in_duration>1.0</fade_in_duration>
+        
         <!-- The stylesheet will describe how to render the feature data. In this case
              we indicate model substitution with density-based scattering. The "density"
              units are instances-per-sqkm. -->
diff --git a/tests/feature_scripted_styling_2.earth b/tests/feature_scripted_styling_2.earth
index 69e428e..2602096 100644
--- a/tests/feature_scripted_styling_2.earth
+++ b/tests/feature_scripted_styling_2.earth
@@ -1,8 +1,7 @@
 <!--
 osgEarth Sample
 
-This one demonstrates how to read feature data from a shapefile and "drape" it
-on the map using the overlay technique.
+Demonstrates how to select a style name using javascript.
 -->
 
 <map name="Feature Overlay Demo" type="geocentric" version="2">
diff --git a/tests/gdal_tiff.earth b/tests/gdal_tiff.earth
index 0613eb0..82534a5 100644
--- a/tests/gdal_tiff.earth
+++ b/tests/gdal_tiff.earth
@@ -8,6 +8,4 @@ Demonstrates the simplest possible use of the GDAL driver to load a GeoTIFF imag
         <url>../data/world.tif</url>
         <caching_policy usage="no_cache"/>
     </image>
-    
-    <options lighting="false"/>
 </map>
diff --git a/tests/min_max_resolutions.earth b/tests/min_max_resolutions.earth
index f5d643d..f674a42 100644
--- a/tests/min_max_resolutions.earth
+++ b/tests/min_max_resolutions.earth
@@ -12,12 +12,12 @@ Please note that usage of Yahoo! map data is subject to Yahoo!'s terms of servic
         <!-- this level will be visible at lower resolutions -->
         <image name="yahoo_sat" driver="yahoo">
             <dataset>satellite</dataset>            
-			<max_level_resolution>4891</max_level_resolution>
+			<max_resolution>4891</max_resolution>
         </image> 
 
         <!-- this level will be visible at higher resolutions -->
         <image name="yahoo_maps" driver="yahoo">            
-			<min_level_resolution>2445</min_level_resolution>
+			<min_resolution>2445</min_resolution>
         </image> 
         
     </image>
diff --git a/tests/readymap.earth b/tests/readymap.earth
index ddc5555..627c18c 100644
--- a/tests/readymap.earth
+++ b/tests/readymap.earth
@@ -21,4 +21,8 @@ http://readymap.org
         <url>http://readymap.org/readymap/tiles/1.0.0/9/</url>
     </elevation>
     
+    <options>
+      <terrain normalize_edges="true"/>
+    </options>
+    
 </map>
diff --git a/tests/wms_naip.earth b/tests/wms_naip.earth
deleted file mode 100644
index 4029c0e..0000000
--- a/tests/wms_naip.earth
+++ /dev/null
@@ -1,13 +0,0 @@
-<!--
-osgEarth Sample.
-Access to the USGS Seamless WMS Server - NAIP Dataset
--->
-<map>  
-  <image name="naip" driver="wms" version="2">
-    <url>http://isse.cr.usgs.gov/arcgis/services/Combined/USGS_EDC_Ortho_NAIP/MapServer/WMSServer?transparent=true</url>
-    <srs>EPSG:4326</srs>
-	<tile_size>256</tile_size>
-	<layers>0</layers>
-	<format>png</format>
-  </image>
-</map>

-- 
osgEarth terrain rendering toolkit



More information about the Pkg-grass-devel mailing list