[SCM] osgearth branch, upstream, updated. upstream/1.4.1-7-gd4e10ff

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


The following commit has been merged in the upstream branch:
commit 7968994d57fef6077726f12b68aa6ca214cf544a
Author: Pirmin Kalberer <pka at sourcepole.ch>
Date:   Mon Apr 8 20:34:08 2013 +0200

    Upstream version 2.4 RC

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb1f5a5..86f7542 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,9 +19,9 @@ SET_PROPERTY( GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake Targets" )
 PROJECT(OSGEARTH)
 
 SET(OSGEARTH_MAJOR_VERSION 2)
-SET(OSGEARTH_MINOR_VERSION 2)
+SET(OSGEARTH_MINOR_VERSION 3)
 SET(OSGEARTH_PATCH_VERSION 0)
-SET(OSGEARTH_SOVERSION     2)
+SET(OSGEARTH_SOVERSION     0)
 
 SET(OSGEARTH_PLUGIN_PREFIX "")
 
@@ -56,7 +56,7 @@ OPTION(OSG_BUILD_PLATFORM_IPHONE_SIMULATOR "Enable IPhoneSDK Simulator support"
 IF(OSG_BUILD_PLATFORM_IPHONE OR OSG_BUILD_PLATFORM_IPHONE_SIMULATOR)
   
   #you need to manually set the default sdk version here
-  SET (IPHONE_SDKVER "5.1")
+  SET (IPHONE_SDKVER "6.0")
   #the below is taken from ogre, it states the gcc stuff needs to happen before PROJECT() is called. I've no clue if we even need it
   # Force gcc <= 4.2 on iPhone
   include(CMakeForceCompiler)
@@ -108,14 +108,16 @@ FIND_PACKAGE(Sqlite3)
 FIND_PACKAGE(ZLIB)
 FIND_PACKAGE(V8)
 
-FIND_PACKAGE(Qt4 4.6)
-IF (QT4_FOUND)
-    INCLUDE(${QT_USE_FILE})
-    SET(QT_ALL_LIBRARIES ${QT_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTOPENGL_LIBRARY})
-ENDIF (QT4_FOUND)
-
 OPTION(OSGEARTH_USE_QT "Enable to use Qt (build Qt-dependent libraries, plugins and examples)" ON)
 
+IF(OSGEARTH_USE_QT)
+    FIND_PACKAGE(Qt4 4.6)
+    IF (QT4_FOUND)
+        INCLUDE(${QT_USE_FILE})
+        SET(QT_ALL_LIBRARIES ${QT_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTOPENGL_LIBRARY})
+    ENDIF (QT4_FOUND)
+ENDIF()
+
 SET (WITH_EXTERNAL_TINYXML FALSE CACHE BOOL "Use bundled or system wide version of TinyXML")
 IF (WITH_EXTERNAL_TINYXML)
     FIND_PACKAGE(TinyXML)
@@ -180,6 +182,9 @@ ENDIF(WIN32)
 SET(OSG_DIR "" CACHE PATH "set to base osg install path")
 
 SET(CMAKE_DEBUG_POSTFIX  "d" CACHE STRING "add a postfix, usually d on windows")
+SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
+SET(CMAKE_RELWITHDEBINFO_POSTFIX "rd" CACHE STRING "add a postfix, usually empty on windows")
+SET(CMAKE_MINSIZEREL_POSTFIX "s" CACHE STRING "add a postfix, usually empty on windows")
 
 FIND_PACKAGE(OSG)
 
@@ -298,7 +303,7 @@ IF(APPLE)
     IF(OSG_BUILD_PLATFORM_IPHONE OR OSG_BUILD_PLATFORM_IPHONE_SIMULATOR)
 
         IF(OSG_BUILD_PLATFORM_IPHONE)
-            SET(CMAKE_OSX_ARCHITECTURES "armv7" CACHE STRING "Build architectures for iOS" FORCE)
+            SET(CMAKE_OSX_ARCHITECTURES "armv7;armv7s" CACHE STRING "Build architectures for iOS" FORCE)
             SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -miphoneos-version-min=4.1 -mno-thumb -arch armv7 -pipe -no-cpp-precomp" CACHE STRING "Flags used by the compiler during all build types." FORCE)
         ELSE()
             #simulator uses i386 architectures
diff --git a/CMakeModules/OsgEarthMacroUtils.cmake b/CMakeModules/OsgEarthMacroUtils.cmake
index 5862c2c..603bcc5 100644
--- a/CMakeModules/OsgEarthMacroUtils.cmake
+++ b/CMakeModules/OsgEarthMacroUtils.cmake
@@ -307,26 +307,39 @@ MACRO(SETUP_EXE IS_COMMANDLINE_APP)
 
 ENDMACRO(SETUP_EXE)
 
-# Takes optional second argument (is_commandline_app?) in ARGV1
+# Taked optional second arg: APPLICATION_FOLDER
+# Takes optional third arg:  (is_commandline_app?) in ARGV2
 MACRO(SETUP_APPLICATION APPLICATION_NAME)
 
-        SET(TARGET_NAME ${APPLICATION_NAME} )
-
-        IF(${ARGC} GREATER 1)
-            SET(IS_COMMANDLINE_APP ${ARGV1})
-        ELSE(${ARGC} GREATER 1)
-            SET(IS_COMMANDLINE_APP 0)
-        ENDIF(${ARGC} GREATER 1)
-
-        SETUP_EXE(${IS_COMMANDLINE_APP})
-
+    SET(TARGET_NAME ${APPLICATION_NAME} )
+    
+    # 2nd arguemnt: application folder name for IDE?
+    IF(${ARGC} GREATER 1)
+        SET(APPLICATION_FOLDER ${ARGV1})
+    ELSE(${ARGC} GREATER 1)
+        SET(APPLICATION_FOLDER ${TARGET_DEFAULT_APPLICATION_FOLDER})
+    ENDIF(${ARGC} GREATER 1)
+
+    # 3rd argument: is it a command-line app?
+    IF(${ARGC} GREATER 2)
+        SET(IS_COMMANDLINE_APP ${ARGV2})
+    ELSE(${ARGC} GREATER 2)
+        SET(IS_COMMANDLINE_APP 0)
+    ENDIF(${ARGC} GREATER 2)
+
+    SETUP_EXE(${IS_COMMANDLINE_APP})
+        
     INSTALL(TARGETS ${TARGET_TARGETNAME} RUNTIME DESTINATION bin  )
 	#Install to the OSG_DIR as well
 	IF(OSGEARTH_INSTALL_TO_OSG_DIR AND OSG_DIR)
 	  INSTALL(TARGETS ${TARGET_TARGETNAME} RUNTIME DESTINATION ${OSG_DIR}/bin)
 	ENDIF(OSGEARTH_INSTALL_TO_OSG_DIR AND OSG_DIR)
 	
-	SET_PROPERTY(TARGET ${TARGET_TARGETNAME} PROPERTY FOLDER "Samples")
+	IF(NOT APPLICATION_FOLDER)
+	    SET(APPLICATION_FOLDER "Examples")
+	ENDIF(NOT APPLICATION_FOLDER)
+	
+	SET_PROPERTY(TARGET ${TARGET_TARGETNAME} PROPERTY FOLDER ${APPLICATION_FOLDER})
 
 ENDMACRO(SETUP_APPLICATION)
 
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..868d6b8
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/osgEarth.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/osgEarth.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/osgEarth"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/osgEarth"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..b1cb9bc
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,190 @@
+ at ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+set I18NSPHINXOPTS=%SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html       to make standalone HTML files
+	echo.  dirhtml    to make HTML files named index.html in directories
+	echo.  singlehtml to make a single large HTML file
+	echo.  pickle     to make pickle files
+	echo.  json       to make JSON files
+	echo.  htmlhelp   to make HTML files and a HTML help project
+	echo.  qthelp     to make HTML files and a qthelp project
+	echo.  devhelp    to make HTML files and a Devhelp project
+	echo.  epub       to make an epub
+	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  text       to make text files
+	echo.  man        to make manual pages
+	echo.  texinfo    to make Texinfo files
+	echo.  gettext    to make PO message catalogs
+	echo.  changes    to make an overview over all changed/added/deprecated items
+	echo.  linkcheck  to check all external links for integrity
+	echo.  doctest    to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "singlehtml" (
+	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\osgEarth.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\osgEarth.ghc
+	goto end
+)
+
+if "%1" == "devhelp" (
+	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished.
+	goto end
+)
+
+if "%1" == "epub" (
+	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The epub file is in %BUILDDIR%/epub.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "text" (
+	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The text files are in %BUILDDIR%/text.
+	goto end
+)
+
+if "%1" == "man" (
+	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The manual pages are in %BUILDDIR%/man.
+	goto end
+)
+
+if "%1" == "texinfo" (
+	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+	goto end
+)
+
+if "%1" == "gettext" (
+	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end
diff --git a/docs/source/about.rst b/docs/source/about.rst
new file mode 100644
index 0000000..d869d5e
--- /dev/null
+++ b/docs/source/about.rst
@@ -0,0 +1,123 @@
+About the Project
+=================
+
+Introduction
+------------
+
+osgEarth_ is a 3D mapping SDK for OpenSceneGraph_ applications.
+It's different than traditional terrain engines in an important way:
+osgEarth_ does not require you to build a 3D terrain model before you
+display it. 
+Instead, it will access the raw data sources at application run time and
+composite them into a 3D map *on the fly*.
+No terrain model is actually stored to disk, though it does use caching
+techniques to speed up the rendering of the map.
+
+The goals of osgEarth_ are to:
+
+- Enable the development of 3D geospatial appliations on top of OpenSceneGraph_.
+- Make it as easy as possible to visualize terrian models and 3D maps.
+- Interoperate with open mapping standards, technologies, and data.
+
+
+**So if it for me?**
+
+So: does osgEarth replace the need for offline terrain database creation tools? In many cases it does.
+
+Consider using osgEarth_ if you need to:
+
+    - Get a terrain base map up and running quickly and easily
+    - Access open-standards map data services like WMS, WCS, or TMS
+    - Integrate locally-stored data with web-service-based data
+    - Incorporate new geospatial data layers at run-time
+    - Run in a "thin-client" environment
+    - Deal with data that may change over time
+    - Integrate with a commercial data provider
+
+
+Community Resources
+-------------------
+
+Since osgEarth_ is a free open source SDK, the source code is available to
+anyone and we welcome and encourage community participation when it comes
+to testing, adding features, and fixing bugs.
+
+**Support Forum**
+
+    The best way to interact with the osgEarth team and the user community is
+    through the `support forum`_. Here are a couple guidelines for using the
+    board:
+
+    * Please sign up for an account and use your real name. You can participate
+      anonymously, but using your real name helps build a stronger community.
+    * Limit yourself to *one topic* per post. Asking multiple questions in one
+      post makes it too hard to keep track of responses.
+    * Be patient!
+
+**OSG Forum**
+
+    Since osgEarth_ is built on top of OpenSceneGraph_, many questions we get
+    on the message boards are *really* OSG questions. We will still try our
+    best to help. But it's worth your while to join the `OSG Mailing List`_ or
+    read the `OSG Forum`_ regularly as well.
+    
+**Social Media**
+
+* Follow `@pelicanmapping`_ on twitter for updates.
+* Add our `Google+ Page`_ to your circles for gallery shots.
+
+**Professional Services**
+
+    The osgEarth team supports its efforts through professional services. At
+    `Pelican Mapping`_ we do custom software development and integration work
+    involving osgEarth_ (and geospatial technologies in general). 
+    We are based in the US but we work with clients all over the world.
+    `Contact us`_ if you need help!
+
+    
+License
+-------
+
+`Pelican Mapping`_ licenses osgEarth_ under the LGPL_ free open source license. 
+
+This means that:
+
+    1. You can link to the osgEarth_ SDK in any commercial or non-commercial
+       application free of charge.
+       
+    2. If you make any changes to osgEarth_ *itself*, you must make those changes
+       available as free open source software under the LGPL license. (Typically
+       this means contributing your changes back to the project, but it is
+       sufficient to host them in a public GitHub clone.)
+       
+    3. If you redistribute the osgEarth_ *source code* in any form, you must
+       include the associated copyright notices and license information
+       unaltered and intact.
+       
+That's it.
+
+    
+Maintainers
+-----------
+
+`Pelican Mapping`_ maintains osgEarth_. We are located in the Washington, DC area.
+
+Pelican is Glenn_, Jason_, Jeff_, and Paul_.
+
+
+.. _osgEarth:        http://osgEarth.org
+.. _OpenSceneGraph:  http://openscenegraph.org
+.. _Pelican Mapping: http://pelicanmapping.com
+.. _LGPL:            http://www.gnu.org/copyleft/lesser.html
+.. _Glenn:           http://twitter.com/#!/glennwaldron
+.. _Jason:           http://twitter.com/#!/jasonbeverage
+.. _Jeff:            http://twitter.com/#!/_jeffsmith
+.. _Paul:            http://twitter.com/#!/p_levy
+.. _ at pelicanmapping: https://twitter.com/pelicanmapping
+.. _Google+ Page:    https://plus.google.com/b/104014917856468748129/104014917856468748129/posts
+
+.. _support forum:    http://forum.osgearth.osg
+.. _OSG Mailing List: http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
+.. _OSG Forum:        http://forum.openscenegraph.org
+.. _Contact us:       http://pelicanmapping.com/?page_id=2
+
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..248611c
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,242 @@
+# -*- coding: utf-8 -*-
+#
+# osgEarth documentation build configuration file, created by
+# sphinx-quickstart on Tue Jan  8 16:20:40 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'osgEarth'
+copyright = u'2013, Pelican Mapping'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '2.4'
+# The full version, including alpha/beta/rc tags.
+release = '2.4'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'osgEarthdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'osgEarth.tex', u'osgEarth Documentation',
+   u'Pelican Mapping', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'osgearth', u'osgEarth Documentation',
+     [u'Pelican Mapping'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'osgEarth', u'osgEarth Documentation',
+   u'Pelican Mapping', 'osgEarth', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
diff --git a/docs/source/data.rst b/docs/source/data.rst
new file mode 100644
index 0000000..f60660a
--- /dev/null
+++ b/docs/source/data.rst
@@ -0,0 +1,139 @@
+Working with Data
+=================
+
+Where to Find Data
+------------------
+
+Help us add useful sources of Free data to this list.
+
+**Raster data**
+
+    * MapQuest_ - MapQuest open aerial imagery and rasterized OpenStreetMap layers
+    
+    * `USGS National Map`_ - Elevation, orthoimagery, hydrography, geographic names, boundaries,
+      transportation, structures, and land cover products for the US.
+    
+    * `NASA EOSDIS`_ - NASA's Global Imagery Browse Services (GIBS) replaces the agency's old
+      `JPL OnEarth`_ site for global imagery products like MODIS.
+       
+    * `NASA BlueMarble`_ - NASA's whole-earth imagery (including topography and bathymetry maps)
+    
+    * `NRL GIDB`_ - US Naval Research Lab's GIDB OpenGIS Web Services
+    
+    * `Natural Earth`_ - Free vector and raster map data at various scales
+    
+    * `Virtual Terrain Project`_ - Various sources for whole-earth imagery
+        
+        
+**Elevation data**
+
+    * `CGIAR`_ - World 90m elevation data derived from SRTM and ETOPO (`CGIAR European mirror`_)
+    
+    * `SRTM30+`_ - Worldwide elevation coverage (including batymetry)
+    
+    * `GLCF`_ - UMD's Global Land Cover Facility (they also have mosaiced LANDSAT data)
+    
+    * `GEBCO`_ - Genearl Batymetry Chart of the Oceans
+
+**Feature data**
+
+    * `OpenStreetMap`_ - Worldwide, community-sources street and land use data (vectors and rasterized tiles)
+    
+    * `DIVA-GIS`_ - Free low-resolution vector data for any country
+    
+    * `Natural Earth`_ - Free vector and raster map data at various scales
+    
+
+.. _CGIAR:                      http://srtm.csi.cgiar.org/
+.. _CGIAR Europoean mirror:     ftp://xftp.jrc.it/pub/srtmV4/
+.. _DIVA-GIS:                   http://www.diva-gis.org/gData
+.. _GEBCO:                      http://www.gebco.net/
+.. _GLCF:                       http://glcf.umiacs.umd.edu/data/srtm/
+.. _OpenStreetMap:              http://openstreetmap.org
+.. _MapQuest:                   http://developer.mapquest.com/web/products/open/map
+.. _NASA EOSDIS:                http://earthdata.nasa.gov/about-eosdis/system-description/global-imagery-browse-services-gibs
+.. _NASA BlueMarble:            http://visibleearth.nasa.gov/view_cat.php?categoryID=1484
+.. _Natural Earth:              http://www.naturalearthdata.com/
+.. _NRL GIDB:                   http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet
+.. _+SRTM30+:                   ftp://topex.ucsd.edu/pub/srtm30_plus/
+.. _USGS National Map:          http://nationalmap.gov/viewer.html
+.. _Virtual Terrain Project:    http://vterrain.org/Imagery/WholeEarth/
+
+----
+
+Tips for Preparing your own Data
+--------------------------------
+
+**Processing Local Source Data**
+
+    If you have geospatial data that you would like to view in osgEarth, you can usually use the GDAL driver.
+    If you plan on doing this, try loading it as-is first.
+    If you find that it's too slow, here are some tips for optimizing your data for tiled access.
+    
+    **Reproject your data**
+
+    osgEarth will reproject your data on your fly if it does not have the necessary
+    coordinate system.  For instance, if you are trying to view a UTM image on a
+    geodetic globe (epsg:4326).  However, osgEarth will run much faster if your data
+    is already in the correct coordinate system.  You can use any tool you want to 
+    reproject your data such as GDAL, Global Mapper or ArcGIS.
+    
+    For example, to reproject a UTM image to geodetic using gdal_warp::
+
+        gdalwarp -t_srs epsg:4326 my_utm_image.tif my_gd_image.tif
+
+    **Build internal tiles**
+    
+    Typically formats such as GeoTiff store their pixel data in scanlines.
+    This generally works well, but because of the tiled approach that osgEarth
+    uses to access the data, you may find that using a tiled dataset will be more
+    efficient as osgEarth doens't need to read nearly as much data from disk to
+    extract a tile.
+    
+    To create a tiled GeoTiff using gdal_translate, issue the following command::
+    
+        gdal_translate -of GTiff -co "TILED=YES" myfile.tif myfile_tiled.tif
+
+    **Build overviews**
+    
+    Adding overviews (also called ''pyramids'' or ''rsets'') will greatly increase
+    the peformance of a datasource in osgEarth.  You can use the
+    `gdaladdo <http://gdal.org/gdaladdo.html>`_ utility to add overviews to a dataset.
+    
+    For example::
+
+        gdaladdo -r average myimage.tif 2 4 8 16
+
+        
+**Building tile sets**
+
+    Another way to speed up imagery and elevation loading in osgEarth is to build **tile sets**.
+    In fact, if you want to serve your data over the network, this is the only way!
+    
+    This process takes the source data and chops it up into a quad-tree hierarchy of discrete
+    *tiles* that osgEarth can load very quickly. Normally, if you load a GeoTIFF (for example),
+    osgEarth has to create the tiles at runtime in order to build the globe; Doing this beforehand
+    means less work for osgEarth when you run your application.
+
+    **osgearth_package**
+
+    *osgearth_package* is a utility that prepares source data for use in osgEarth. 
+    It is **optional** - you can run osgEarth against your raw source data 
+    and it will work fine - but you can use *osgearth_package* to build optimized 
+    tile sets that will maximize performance in most cases. Usage::
+    
+        osgearth_package file.earth --tms --out output_folder
+
+    This will load each of the data sources in the the earth file 
+    (``file.earth`` in this case) and generate a TMS repository for each under the
+    folder ``output_folder``. You can also specify options:
+    
+        --out path                          Root output folder of the TMS repo
+        --ext extension                     Output file extension
+        --max-level level                   Maximum level of detail
+        --bounds xmin ymin xmax ymax        Bounds to package (in map coordinates; default=entire map)
+        --out-earth                         Generate an output earth file referencing the new repo
+        --overwrite                         Force overwriting of existing files
+        --keep-empties                      Writes fully transparent image tiles (normally discarded)
+        --db-options                        An optional OSG options string
+        --quiet                             Suppress progress reporting
diff --git a/docs/source/developer/custom_driver.rst b/docs/source/developer/custom_driver.rst
new file mode 100644
index 0000000..cf99fa3
--- /dev/null
+++ b/docs/source/developer/custom_driver.rst
@@ -0,0 +1,4 @@
+Building a Custom Driver
+========================
+
+TBD.
diff --git a/docs/source/developer/index.rst b/docs/source/developer/index.rst
new file mode 100644
index 0000000..b0212dc
--- /dev/null
+++ b/docs/source/developer/index.rst
@@ -0,0 +1,11 @@
+Developer Topics
+================
+
+.. toctree::
+   :maxdepth: 2
+   
+   maps
+   utilities
+   shader_composition
+   custom_driver
+   qt_integration
diff --git a/docs/source/developer/maps.rst b/docs/source/developer/maps.rst
new file mode 100644
index 0000000..09e1b4a
--- /dev/null
+++ b/docs/source/developer/maps.rst
@@ -0,0 +1,123 @@
+Working with Maps
+=================
+
+A **map** is the central data model in osgEarth.
+It is a container for image, elevation, and feature layers.
+
+Loading a Map from an Earth File
+--------------------------------
+
+The easiest way to render a Map is to load it from an *earth file*.
+Since osgEarth uses OpenSceneGraph plugins, you can do this with a single line of code::
+
+    osg::Node* globe = osgDB::readNodeFile("myglobe.earth");
+
+You now have an ``osg::Node`` that you can add to your scene graph and display.
+Seriously, it really is that simple!
+
+This method of loading a Map is, more often than not, all that an application will
+need to do. However if you want to create your Map using the API, read on.
+
+
+Programmatic Map Creation
+-------------------------
+
+osgEarth provides an API for creating Maps at runtime.
+
+The basic steps to creating a Map with the API are:
+
+1. Create a Map object
+2. Add imagery and elevation layers to the Map as you see fit
+3. Create a ``MapNode`` that will render the Map object
+4. Add your ``MapNode`` to your scene graph.
+
+You can add layers to the map at any time::
+
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
+    
+    #include <osgEarth/Map>
+    #include <osgEarth/MapNode>
+    #include <osgEarthDrivers/tms/TMSOptions>
+    #include <osgEarthDrivers/gdal/GDALOptions>
+
+    using namespace osgEarth;
+    using namespace osgEarth::Drivers;
+    ...
+
+    // Create a Map and set it to Geocentric to display a globe
+    Map* map = new Map();
+
+    // Add an imagery layer (blue marble from a TMS source)
+    {
+        TMSOptions tms;
+        tms.url() = "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/satellite/";
+        ImageLayer* layer = new ImageLayer( "NASA", tms );
+        map->addImageLayer( layer );
+    }
+
+    // Add an elevationlayer (SRTM from a local GeoTiff file)
+    {
+        GDALOptions gdal;
+        gdal.url() = "c:/data/srtm.tif";
+        ElevationLayer* layer = new ElevationLayer( "SRTM", gdal );
+        map->addElevationLayer( layer );
+    }
+
+    // Create a MapNode to render this map:
+    MapNode* mapNode = new MapNode( map );
+    ...
+    
+    viewer->setSceneData( mapNode );
+
+    
+Working with a MapNode at Runtime
+----------------------------------
+
+A ``MapNode`` is the scene graph node that renders a ``Map``. Whether you loaded your
+map from an Earth File or created it using the API, you can access the Map and its
+``MapNode`` at runtime to make changes. If you did not explicitly create a ``MapNode``
+using the API, you will first need to get a reference to the ``MapNode`` to work with.
+Use the static ``get`` function::
+
+    // Load the map
+    osg::Node* loadedModel = osgDB::readNodeFile("mymap.earth");
+
+    // Find the MapNode
+    osgEarth::MapNode* mapNode = MapNode::get( loadedModel );
+
+    
+Once you have a reference to the ``MapNode``, you can get to the map::
+
+    // Add an OpenStreetMap image source
+    TMSOptions driverOpt;
+    driverOpt.url() = "http://tile.openstreetmap.org/";
+    driverOpt.tmsType() = "google";
+
+    ImageLayerOptions layerOpt( "OSM", driverOpt );
+    layerOpt.profile() = ProfileOptions( "global-mercator" );
+
+    ImageLayer* osmLayer = new ImageLayer( layerOpt );
+    mapNode->getMap()->addImageLayer( osmLayer );
+
+    
+You can also remove or re-order layers::
+
+    // Remove a layer from the map.  All other layers are repositioned accordingly
+    mapNode->getMap()->removeImageLayer( layer );
+
+    // Move a layer to position 1 in the image stack
+    mapNode->getMap()->moveImageLayer( layer, 1 );
+
+
+Working with Layers
+-------------------
+
+The ``Map`` contains ``ImageLayer`` and ``ElevationLayer`` objects.
+These contain some properties that you can adjust at runtime.
+For example, you can toggle a layer on or off or adjust an ``ImageLayer`` opacity using the API::
+
+    ImageLayer* layer;
+    ...
+    layer->setOpacity( 0.5 );  // makes the layer partially transparent
+
diff --git a/docs/source/developer/qt_integration.rst b/docs/source/developer/qt_integration.rst
new file mode 100644
index 0000000..2e6097d
--- /dev/null
+++ b/docs/source/developer/qt_integration.rst
@@ -0,0 +1,5 @@
+Qt Integration
+==============
+
+TBD.
+
diff --git a/docs/source/developer/shader_composition.rst b/docs/source/developer/shader_composition.rst
new file mode 100644
index 0000000..3a7ac65
--- /dev/null
+++ b/docs/source/developer/shader_composition.rst
@@ -0,0 +1,199 @@
+Shader Composition
+==================
+
+osgEarth uses GLSL shaders in several of its rendering modes. By default,
+osgEarth will detect the capabilities of your graphics hardware and
+automatically select an appropriate mode to use.
+
+Since osgEarth relies on shaders, and since you as the developer may wish
+to use your own shader code as well, osgEarth provides a *shader composition*
+framework. This allows you a great deal of flexibility when incorporating
+your own shaders into osgEarth.
+
+There are several ways to integrate your own shader code into osgEarth.
+We discuss these below. But first it is important to understand the basics of
+osgEarth's shader composition framework.
+
+Framework Basics
+----------------
+
+osgEarth installs default shaders for rendering. The default shaders are shown
+below. The ``LOCATION_*`` designators allow you to inject functions at
+various points in the shader's execution.
+
+Here is the pseudo-code for osgEarth's built-in shaders::
+
+    // VERTEX SHADER:
+
+    void main(void)
+    {
+        vec4 vertex = gl_Vertex;
+
+        // "LOCATION_VERTEX_MODEL" user functions are called here:
+        model_func_1(vertex);
+        ...
+
+        vertex = gl_ModelViewMatrix * vertex;
+
+        // "LOCATION_VERTEX_VIEW" user functions are called here:
+        view_func_1(vertex);
+        ...
+
+        vertes = gl_ProjectionMatrix * vertex;
+        
+        // "LOCATION_VERTEX_CLIP" user functions are called last:
+        clip_func_1(vertex);
+        ...
+        
+        gl_Position = vertex;
+    }  
+
+
+    // FRAGMENT SHADER:
+
+    void main(void)
+    {
+        vec4 color = gl_Color;
+        ...
+
+        // "LOCATION_FRAGMENT_COLORING" user functions are called here:
+        coloring_func_1(color);
+        ...
+
+        // "LOCATION_FRAGMENT_LIGHTING" user functions are called here:
+        lighting_func_1(color);
+        ...
+
+        gl_FragColor = color;
+    }  
+
+
+VirtualProgram
+--------------
+
+osgEarth include an OSG state attribute called ``VirtualProgram`` that performs
+the runtime shader composition. Since ``VirtualProgram`` is an ``osg::StateAttribute``,
+you can attach one to any node in the scene graph. Shaders that belong to a
+``VirtualProgram`` can override shaders lower down on the attribute stack
+(i.e., higher up in the scene graph). In the way you can override individual shader
+functions in osgEarth.
+
+The sections below on integration will demonstrate how to use ``VirtualProgram``.
+
+
+Integrating Custom Shaders
+--------------------------
+
+There are two ways to use shader composition in osgEarth.
+
+* Injecting user functions
+* Overriding osgEarth's built-in functions with a custom ``ShaderFactory``
+
+ 
+Injecting User Functions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the core shader code above, osgEarth calls into user functions.
+These don't exist in the default shaders that osgEarth generates;
+rather, they represent code that you as the developer can "inject"
+into various locations in the built-in shaders.
+
+For example, let's use User Functions to create a simple "haze" effect.
+(NOTE: see this example in its entirety in osgearth_shadercomp.cpp)::
+
+    static char s_hazeVertShader[] =
+        "varying vec3 v_pos; \n"
+        "void setup_haze(inout vec4 vertexVIEW) \n"
+        "{ \n"
+        "    v_pos = vec3(vertexVIEW); \n"
+        "} \n";
+
+    static char s_hazeFragShader[] =
+        "varying vec3 v_pos; \n"
+        "void apply_haze(inout vec4 color) \n"
+        "{ \n"
+        "    float dist = clamp( length(v_pos)/10000000.0, 0, 0.75 ); \n"
+        "    color = mix(color, vec4(0.5, 0.5, 0.5, 1.0), dist); \n"
+        "} \n";
+
+    osg::StateAttribute*
+    createHaze()
+    {
+        osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
+
+        vp->setFunction( "setup_haze", s_hazeVertShader, osgEarth::ShaderComp::LOCATION_VERTEX_VIEW);
+        vp->setFunction( "apply_haze", s_hazeFragShader, osgEarth::ShaderComp::LOCATION_FRAGMENT_LIGHTING);
+
+        return vp;
+    }
+
+    ...
+    sceneGraph->getOrCreateStateSet()->setAttributeAndModes( createHaze() );
+
+In this example, the function ``setup_haze`` is called from the core vertex shader
+after the built-in vertex functions. The ``apply_haze`` function gets called from
+the core fragment shader after the built-in fragment functions.
+
+There are FIVE injection points, as follows:
+
++----------------------------------------+-------------+------------------------------+
+| Location                               | Shader Type | Signature                    |
++========================================+=============+==============================+
+| ShaderComp::LOCATION_VERTEX_MODEL      | VERTEX      | void func(inout vec4 vertex) |
++----------------------------------------+-------------+------------------------------+
+| ShaderComp::LOCATION_VERTEX_VIEW       | VERTEX      | void func(inout vec4 vertex) |
++----------------------------------------+-------------+------------------------------+
+| ShaderComp::LOCATION_VERTEX_CLIP       | VERTEX      | void func(inout vec4 vertex) |
++----------------------------------------+-------------+------------------------------+
+| ShaderComp::LOCATION_FRAGMENT_COLORING | FRAGMENT    | void func(inout vec4 color)  |
++----------------------------------------+-------------+------------------------------+
+| ShaderComp::LOCATION_FRAGMENT_LIGHTING | FRAGMENT    | void func(inout vec4 color)  |
++----------------------------------------+-------------+------------------------------+
+
+Each VERTEX locations let you operate on the vertex in a particular *coordinate space*. 
+You can alter the vertex, but you *must* leave it in the same space.
+
+:MODEL:  Vertex is the raw, untransformed values from the geometry.
+:VIEW:   Vertex is relative to the eyepoint, which lies at the origin (0,0,0) and 
+         points down the -Z axis. In VIEW space, the orginal vertex has been
+         transformed by ``gl_ModelViewMatrix``.
+:CLIP:   Post-projected clip space. CLIP space lies in the [-w..w] range along all
+         three axis, and is the result of transforming the original vertex by
+         ``gl_ModelViewProjectionMatrix``.
+
+
+Customizing the Shader Factory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is amore advanced topic.
+If you want to replace osgEarth's built-in shader functions, you can install a custom
+``ShaderFactory``. The ``ShaderFactory`` is stored in the osgEarth ``Registry`` and contains
+all the methods for creating the built-in functions. You can install your own ``ShaderFactory``
+like so::
+
+    #include <osgEarth/ShaderFactory>
+    ...
+
+    class CustomShaderFactory : public osgEarth::ShaderFactory
+    {
+        ... override desired methods here ...
+    };
+    ...
+
+    osgEarth::Registry::instance()->setShaderFactory( new CustomShaderFactory() );
+
+This method is good for replacing osgEarth's built-in lighting shader code.
+HOWEVER: be aware that override the built-in texturing functions may not work.
+This is because osgEarth's image layer composition mechanisms override these methods
+themselves to perform layer rendering.
+
+
+System Uniforms
+---------------
+
+In addition the the OSG system uniforms (which all start with "osg_"), osgEarth
+provides various uniforms. They are:
+
+  :osgearth_LightingEnabled:     whether GL lighting is enabled (bool)
+  :osgearth_CameraElevation:     distance from camera to ellipsoid/Z=0 (float)
+
diff --git a/docs/source/developer/utilities.rst b/docs/source/developer/utilities.rst
new file mode 100644
index 0000000..b647f45
--- /dev/null
+++ b/docs/source/developer/utilities.rst
@@ -0,0 +1,126 @@
+Utilities SDK
+=============
+
+The osgEarth *Utils* namespace includes a variety of useful classes for interacting
+with the map. None of these are strictly necessary for using osgEarth, but they do
+make it easier to perform some common operations.
+
+DataScanner
+-----------
+
+The ``DataScanner`` will recursively search a directory tree on the local filesystem
+for files that it can load as ``ImageLayer`` objects. It is a quick and easy way to 
+load a full directory of images at layers.
+
+**NOTE** that only the *MP Terrain Engine* supports an unlimited number of image layers,
+so it is wise to use that engine in conjunction with the DataScanner.
+
+Use DataScanner like this::
+
+    DataScanner scanner;
+    ImageLayerVector imageLayers;
+    scanner.findImageLayers( rootFolder, extensions, imageLayers );
+    
+You can then add the image layes to your ``Map`` object.
+
+The ``extensions`` parameter lets you filter files by extension. For example, pass in 
+"tif,ecw" to only consider files with those extensions. Separate multiple extensions
+with a comma.
+
+
+Formatters
+----------
+
+Use *Formatters* to format geospatial coordinates as a string. There are two stock formatters,
+the ``LatLongFormatter`` and the ``MGRSFormatter``. A formatter takes a ``GeoPoint`` and
+returns a ``std::string`` like so::
+
+    LatLongFormatter formatter;
+    GeoPoint point;
+    ....
+    std::string = formatter.format( point );
+
+LatLongFormatter
+~~~~~~~~~~~~~~~~
+
+The ``LatLongFormatter`` takes coordinates and generates a string. It supports the following
+formats:
+
+    :FORMAT_DECIMAL_DEGREES:            34.04582
+    :FORMAT_DEGREES_DECIMAL_MINUTES:    34.20:30
+    :FORMAT_DEGREES_MINUTES_SECONDS:    34:14:30
+
+You can also specify options for the output string:
+
+    :USE_SYMBOLS:   Use the degrees, minutes and seconds symbology
+    :USE_COLONS:    Use colons between the components
+    :USE_SPACES:    Use spaces between the components
+
+
+MGRSFormatter
+~~~~~~~~~~~~~
+
+The ``MGRSFormatter`` echos a string according to the `Military Grid Reference System`_. 
+Technically, an MGRS coordinate represents a *region* rather than an exact point, so you
+have to specifiy a *precision* qualifier to control the size of the represented region.
+Example::
+
+    MGRSFormatter mgrs( MGRFormatter::PRECISION_1000M );
+    std::string str = mgrs.format( geopoint );
+
+.. _Military Grid Reference System: http://en.wikipedia.org/wiki/Military_grid_reference_system
+
+
+Graticules
+----------
+
+LineOfSight
+-----------
+
+MouseCoordsTool
+---------------
+
+The ``MouseCoordsTool`` reports the map coordinates under the mouse (or other pointing device).
+Install a callback to respond to the reports. ``MouseCoordsTool`` is an ``osgGA::GUIEventHandler``
+that you can install on a ``Viewer`` or any ``Node``, like so::
+
+    MouseCoordsTool* tool = new MouseCoordsTool();
+    tool->addCallback( new MyCallback() );
+    viewer.addEventHandler( tool );
+    
+Create your own callback to respond to reports. Here is an example that prints the X,Y under the
+mouse to a *Qt* status bar::
+
+    struct PrintCoordsToStatusBar : public MouseCoordsTool::Callback
+    {
+    public:
+        PrintCoordsToStatusBar(QStatusBar* sb) : _sb(sb) { }
+
+        void set(const GeoPoint& p, osg::View* view, MapNode* mapNode)
+        {
+            std::string str = osgEarth::Stringify() << p.y() << ", " << p.x();
+            _sb->showMessage( QString(str.c_str()) );
+        }
+
+        void reset(osg::View* view, MapNode* mapNode)
+        {
+            _sb->showMessage( QString("out of range") );
+        }
+
+        QStatusBar* _sb;
+    };
+
+For your convenience, ``MouseCoordsTool`` also comes with a stock callback that will
+print the coords to ``osgEarthUtil::Controls::LabelControl``. You can even pass a
+``LabelControl`` to the contructor to make it even easier.
+
+
+ObjectLocator
+-------------
+
+SkyNode
+-------
+
+TerrainProfile
+--------------
+
diff --git a/docs/source/faq.rst b/docs/source/faq.rst
new file mode 100644
index 0000000..08032eb
--- /dev/null
+++ b/docs/source/faq.rst
@@ -0,0 +1,102 @@
+FAQ
+===
+
+Miscellaneous
+-------------
+
+**How do make the globe transparent so I can see underground?**
+
+	By default, the globe will be opaque white when there are no image layers, or when all the image
+	layers have their opacities set to zero. To make the underlying globe transparent, you need to 
+	enable *terrain blending*, like so::
+	
+		<map>
+		  <options>
+		    <terrain blending="true" ...
+			
+
+Other Terrain Technologies
+--------------------------
+
+**Does osgEarth work with VirtualPlanetBuilder?**
+
+	VirtualPlanetBuilder_ (VPB) is a command-line terrain generation tool. Before osgEarth
+	came along, VPB	was probably the most-used open source tool for building terrains for
+	OSG appliations. We	mention is here because many people ask questions about loading 
+	VPB models or transitioning from VPB to osgEarth.
+	
+	osgEarth differs from VPB in that:
+	
+	* VPB builds static terrain models and saves them to disk. osgEarth generates terrain on
+	  demand as your application runs; you do not (and cannot) save a model to disk.
+	* Changing a VPB terrain generally requires that you rebuild the model. osgEarth does not
+	  require a preprocessing step since it builds the terrain at run time.
+	* osgEarth and VPB both use *GDAL* to read many types of imagery and elevation data from
+	  the local file system. osgEarth also supports network-based data sources through its
+	  plug-in framework.
+
+	osgEarth has a *VPB driver* for "scraping" elevation and imagery tiles from a VPB model.
+	See the ``vpb_earth_bayarea.earth`` example in the repo for usage.
+	
+	**Please Note** that this driver only exists as a **last resort** for people that have a VPB
+	model but no longer have access to the source data from which it was built. If at all
+	possible you should feed your source data directly into osgEarth instead of using the VPB
+	driver.
+	
+**Can osgEarth load TerraPage or MetaFlight?**
+
+	osgEarth cannot natively load TerraPage (TXP) or MetaFlight. However, osgEarth does have a
+	"bring your own terrain" plugin that allows you to load an external model and use it as your
+	terrain. The caveat is that since osgEarth doesn't know anything about your terrain model, you
+	will not be able to use some of the features of osgEarth (like being able to add or remove layers).
+	
+	For usage formation, please refer to the ``byo.earth`` example in the repo.
+
+.. _VirtualPlanetBuilder:	http://www.openscenegraph.com/index.php/documentation/tools/virtual-planet-builder
+
+
+Community
+---------
+
+**What is the "best practice" for using GitHub?**
+
+	The best way to work with the osgEarth repository is to make your own clone on GitHub
+	and to work from that clone. Why not work directly against the main repository? You
+	can, but if you need to make changes, bug fixes, etc., you will need your own clone
+	in order to issue Pull Requests.
+	
+	1. Create your own GitHub account and log in.
+	2. Clone the osgEarth repo.
+	3. Work from your clone. Update it from the main repository peridocially.
+	
+**How do I submit changes to osgEarth?**
+
+	We accept contributions and bug fixes through GitHub's *pull request* mechanism.
+
+	First you need your own GitHub account and a clone of the repo (see above). Next,
+	follow these guidelines:
+	
+	1. Create a *branch* in which to make your changes.
+	2. Make the change.
+	3. Issue a *pull request* against the main osgEarth repository.
+	4. We will review the *PR* for inclusion.
+
+	If we decide NOT to include your submission, you can still keep it in your cloned
+	repository and use it yourself. Doing so maintains compliance with the osgEarth
+	license since your changes are still available to the public - even if they are
+	not merged into the master repository.
+	
+Licensing
+---------
+
+**Can I use osgEarth in a commercial product?**
+
+	Yes. The license permits use in a commercial product. The only requirement is that
+	any changes you make to the actual osgEarth library *itself* be made available
+	under the same license as osgEarth. You do *not* need to make other parts of your
+	application public.
+	
+**Can I use osgEarth in an iOS app?**
+
+	Yes. Apple's policy requires only statically linked libraries. Technically, the
+	LGPL does not support static linking, but we grant an exception in this case.
diff --git a/docs/source/howtos.rst b/docs/source/howtos.rst
new file mode 100644
index 0000000..ca05e51
--- /dev/null
+++ b/docs/source/howtos.rst
@@ -0,0 +1,4 @@
+How To's
+========
+
+
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..d2ed9f0
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,33 @@
+.. osgEarth documentation master file, created by
+   sphinx-quickstart on Tue Jan  8 16:20:40 2013.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+osgEarth - a C++ Terrain SDK
+============================
+Welcome to the osgEarth_ documentation project.
+
+osgEarth_ is a big SDK with a LOT of functionality.  Keeping up on the documentation is
+not easy! So now we've moved the docs right into the osgEarth Git repository to make
+it easier for the osgEarth team and user community to help. Check the links at the 
+bottom of the sidebar.
+
+Table of Contents
+-----------------
+
+.. toctree::
+   :maxdepth: 2
+
+   about
+   startup
+   user/index
+   developer/index
+   data
+   howto
+   references/index
+   faq
+   releasenotes
+
+
+.. _osgEarth: http://osgearth.org
+.. _GitHub:   http://github.com/gwaldron/osgearth
diff --git a/docs/source/install.rst b/docs/source/install.rst
new file mode 100644
index 0000000..38f5cc9
--- /dev/null
+++ b/docs/source/install.rst
@@ -0,0 +1,23 @@
+Building osgEarth
+===============
+osgEarth is a cross-platform library. It uses the  CMake cross-platform build system, version 2.8 or newer. This is the same build system that  OpenSceneGraph uses.
+
+Get the source code
+----------------
+#Option 1: Use GIT
+
+osgEarth source is hosted on GitHub. You will need a git client to access it. We recommend  TortoiseGit for Windows users.
+
+To clone the repository, point your client at git://github.com/gwaldron/osgearth.git
+
+#Option 2: Download a tarball
+
+To download a tarball, visit http://github.com/gwaldron/osgearth/tags and select the one you want.
+
+
+Get the dependencies
+-------------
+Words can have *emphasis in italics* or be **bold** and you can define
+code samples with back quotes, like when you talk about a command: ``sudo`` 
+gives you super user powers! 
+
diff --git a/docs/source/ios.rst b/docs/source/ios.rst
new file mode 100644
index 0000000..a4e6aba
--- /dev/null
+++ b/docs/source/ios.rst
@@ -0,0 +1,104 @@
+Building osgEarth (and OSG) for iOS
+===================================
+
+When building osgEarth for iOS it is important to note that it requires two separate builds to build for both the simulator and devices.  The instructions below are configured to build the libraries for use with devices.  Building for use in the simulator is the same but requires pointing to the proper libraries when building.
+
+The steps to buld for use on devices are as follows:
+
+#. **Install CMake 2.8.8 or above**
+     | http://www.cmake.org
+     | Or install `MacPorts <http://guide.macports.org/#installing.macports>`_ and then ``sudo ports install cmake``
+
+#. **Install XCode 4.5.1**
+     | Download from AppStore
+
+#. **Get Dependencies**
+     | Prebuilt dependencies courtesy of `Thomas Hogarth <http://www.hogbox.co.uk>`_ can be downloaded here: https://s3.amazonaws.com/pelican-downloads/ios-3rdParty.zip
+
+#. **Download OSG trunk**
+   ::
+      mkdir osgearth-build
+      cd ./osgearth-build
+      svn checkout http://www.openscenegraph.org/svn/osg/OpenSceneGraph/trunk osg-ios
+
+#. **Edit CMakeList.txt to point to your version of iOS SDK**
+     | Check ``/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs``
+     | Open CMakeList.txt and change line 236 to your SDK version: ``(SET (IPHONE_SDKVER "5.1" CACHE STRING "IOS SDK-Version")``
+     | For the lib tiff plugin it is also currently necessary to copy line 587 and paste inside the else statement at line 593 (submited to osg trunk)
+
+#. **Run CMake to generate OSG XCode Project**
+     | Use below command line to generate static GLES2 build for iOS devices, change 3rdparty paths to reflect your 3rdparty path
+     ::
+
+      cd ./osg-ios
+
+      cmake ./ -G Xcode -DOSG_BUILD_PLATFORM_IPHONE:BOOL=ON \
+      -DBUILD_OSG_APPLICATIONS:BOOL=OFF \
+      -DOSG_WINDOWING_SYSTEM:STRING=IOS \
+      -DOSG_DEFAULT_IMAGE_PLUGIN_FOR_OSX="imageio" \
+      -DOSG_GL1_AVAILABLE:BOOL=OFF \
+      -DOSG_GL2_AVAILABLE:BOOL=OFF \
+      -DOSG_GLES1_AVAILABLE:BOOL=OFF \
+      -DOSG_GLES2_AVAILABLE:BOOL=ON \
+      -DOSG_GL_DISPLAYLISTS_AVAILABLE:BOOL=OFF \
+      -DOSG_GL_FIXED_FUNCTION_AVAILABLE:BOOL=OFF \
+      -DOSG_GL_LIBRARY_STATIC:BOOL=OFF \
+      -DOSG_GL_MATRICES_AVAILABLE:BOOL=OFF \
+      -DOSG_GL_VERTEX_ARRAY_FUNCS_AVAILABLE:BOOL=OFF \
+      -DOSG_GL_VERTEX_FUNCS_AVAILABLE:BOOL=OFF \
+      -DCURL_INCLUDE_DIR:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/curl-ios-device/include" \
+      -DCURL_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/curl-ios-device/lib/libcurl.a" \
+      -DFREETYPE_INCLUDE_DIR_freetype2:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/freetype-ios-universal/include/freetype" \
+      -DFREETYPE_INCLUDE_DIR_ft2build:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/freetype-ios-universal/include" \
+      -DFREETYPE_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/freetype-ios-universal/lib/libFreeType_iphone_universal.a" \
+      -DTIFF_INCLUDE_DIR:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/tiff-ios-device/include" \
+      -DTIFF_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/tiff-ios-device/lib/libtiff.a" \
+      -DGDAL_INCLUDE_DIR:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/gdal-ios-device/include" \
+      -DGDAL_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/gdal-ios-device/lib/libgdal.a" \
+      -DDYNAMIC_OPENSCENEGRAPH:BOOL=OFF \
+      -DDYNAMIC_OPENTHREADS:BOOL=OFF
+
+#. **Open XCode project generated and run BUILD_ALL target**
+
+#. **Download osgEarth trunk**
+   ::
+      cd ../
+      git clone git://github.com/gwaldron/osgearth.git osgearth-ios
+
+#. **Edit iOS SDK version in ``./osgearth-ios/CMakeList.txt`` (see item 5)**
+
+#. **Run CMake to generate osgEarth Xcode project**
+     | Use below command line to generate static bulild for iOS device, change 3rdparty paths to reflect your 3rdparty path
+     ::
+     
+      cmake ./ -G Xcode -DOSG_BUILD_PLATFORM_IPHONE:BOOL=ON \
+      -DOSG_DIR:PATH="/Users/hogbox/Documents/osgearth-build/osg-ios" \
+      -DCURL_INCLUDE_DIR:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/curl-ios-device/include" \
+      -DCURL_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/curl-ios-device/lib/libcurl.a" \
+      -DGDAL_INCLUDE_DIR:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/gdal-ios-device/include" \
+      -DGDAL_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/gdal-ios-device/lib/libgdal.a" \
+      -DGEOS_INCLUDE_DIR:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/geos-ios-device/include/source/headers" \
+      -DGEOS_LIBRARY:PATH="/Users/hogbox/Documents/osgearth-build/3rdParty/geos-ios-device/lib/libGEOS_3.2.a" \
+      -DOSGEARTH_BUILD_APPLICATION_BUNDLES:BOOL=OFF \
+      -DDYNAMIC_OSGEARTH:BOOL=OFF \
+      -DOSGEARTH_USE_QT:BOOL=OFF
+
+#. **Open generated Xcode project and build**
+     | For now do not build application targets as they will generate errors (CMake can not currently generate valid application targets for iOS)
+     | Select *OSGEARTH* project in navigator view (top of tree on left)
+     | Select *Add Target*
+     | Select *Aggregate Target* in the *Other* section, name it lib-build (or whatever)
+     | Select new target and select *Build Phases*, *Target Dependancies*, *+*
+     | Select all the libs and plugins 
+     | Select the new target as the current build target (combo box to right of the play/run button)
+     | Build
+
+#. **Open and build example project**
+     | ``osgearth-ios/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj``
+     | 
+     | Edit *Header Search Paths* in build settings point to your osg and osgearth folders
+     | Edit *Library Search Paths* in build settings point to your osg and osgearth folders
+     | Link to Accelerate.framework (fix sent)
+     | Link to MobileCoreServices.framework (fix sent)
+     | remove armv7s build if you are using freetypes
+     | build
diff --git a/docs/source/references/colorfilters.rst b/docs/source/references/colorfilters.rst
new file mode 100644
index 0000000..343c89c
--- /dev/null
+++ b/docs/source/references/colorfilters.rst
@@ -0,0 +1,102 @@
+Color Filter Reference
+======================
+A *color fitler* is an inline, GLSL processor for an ImageLayer. 
+The osgEarth terrain engine runs each image tile through its layer's
+color filter as it's being rendered on the GPU. You can chain color
+filter together to form an image processing pipeline.
+
+osgEarth comes with several stock filters; you can create your own
+by implementing the ``osgEarth::ColorFilter`` interface.
+
+Here is how to use a color filter in an earth file::
+
+    <image driver="gdal" name="world">
+        <color_filters>
+            <chroma_key r="1" g="1" b="1" distance=".1"/>
+        </color_filters>
+    </image>
+
+
+Stock color filters:
+
+ * BrightnessContrast_
+ * ChromaKey_
+ * CMYK_
+ * Gamma_
+ * GLSL_
+ * HSL_
+ * RGB_
+
+
+BrightnessContrast
+------------------
+This filter adjusts the brightness and contrast of the image::
+
+    <brightness_contrast b="0.7" c="1.2"/>
+    
+The ``b`` and ``c`` properties are *percentages* of the incoming value.
+For example, ``c="1.2"`` means to increase the contrast by 20%.
+
+
+ChromaKey
+---------
+This filter matches color values are makes fragments to transparent,
+providing a kind of "green-screen" effect::
+
+    <chroma_key r="1.0" g="0.0" b="0.0" distance="0.1"/>
+    
+In this example, we find all red pixels and turn them transparent. 
+The ``distance`` property searches for colors close to the specified
+color. Set it to Zero for exact matches only.
+
+
+CMYK
+----
+This filter offsets the CMYK (cyan, magenta, yellow, black) color levels::
+
+    <cmyk y="-0.1"/>
+    
+Here we are lowering the "yellowness" of the fragment by 0.1. Valid range
+is [-1..1] for each of ``c``, ``m``, ``y``, and ``k``.
+
+
+Gamma
+-----
+This filter performs gamma correction. You can specify a *gamma* value for
+each of ``r``, ``g``, or ``b``, or you can adjust them all together::
+
+    <gamma rgb="1.3"/>
+
+
+GLSL
+----
+The GLSL filter lets you embed custom GLSL code so you can adjust the 
+color value in any way you like. Simply write a GLSL code block
+that operates on the RGBA color variable ``inout vec4 color``::
+
+    <glsl>
+        color.rgb *= pow(color.rgb, 1.0/vec3(1.3));
+    </glsl>
+
+This example does exactly the same thing as the Gamma_ filter but 
+using directly GLSL code.
+
+
+HSL
+---
+This filter offsets the HSL (hue, saturation, lightness) levels::
+
+    <hsl s="0.1" l="0.1"/>
+    
+This example adds a little more color saturation and brightens the
+fragment a bit as well. Valid range is [-1..1] for each of ``h``, ``s``, and ``l``.
+
+
+RGB
+---
+This filter offsets the RGB (red, green, blue) color levels::
+
+    <rgb r="0.1" b="-0.5"/>
+    
+This example adds a little bit of red and reduces the blue channel. Valid
+range is [-1..1] for each of ``r``, ``g``, and ``b``.
diff --git a/docs/source/references/earthfile.rst b/docs/source/references/earthfile.rst
new file mode 100644
index 0000000..599c6b4
--- /dev/null
+++ b/docs/source/references/earthfile.rst
@@ -0,0 +1,404 @@
+Earth File Reference Guide
+--------------------------
+
+Map
+~~~
+The *map* is the top-level element in an earth file.
+
+.. parsed-literal::
+
+    <map name    = "my map"
+         type    = "geocentric"
+         version = "2" >
+         
+        <:ref:`options   <MapOptions>`>
+        <:ref:`image     <ImageLayer>`>
+        <:ref:`elevation <ElevationLayer>`>
+        <:ref:`model     <ModelLayer>`>
+        <:ref:`mask      <MaskLayer>`>
+        
+
++------------------------+--------------------------------------------------------------------+
+| Property               | Description                                                        |
++========================+====================================================================+
+| name                   | Readable name. No effect on rendering.                             |
++------------------------+--------------------------------------------------------------------+
+| type                   | Coordinate system type.                                            |
+|                        |   :geocentric:  Render an ellipsoidal globe.                       |
+|                        |   :projected:   Render a "flat", projected map.                    |
++------------------------+--------------------------------------------------------------------+
+| version                | Earth file version. default = "2". Set this to load an older earth |
+|                        | file format.                                                       |
++------------------------+--------------------------------------------------------------------+
+
+
+.. _MapOptions:
+
+Map Options
+~~~~~~~~~~~
+These options control both the Map Model and the rendering properties associated with
+the entire map.
+
+.. parsed-literal::
+
+    <map>
+        <options lighting                = "true"
+                 elevation_interpolation = "bilinear"
+                 elevation_tile_size     = "8"
+                 overlay_texture_size    = "4096" >
+
+            <:ref:`profile <Profile>`>
+            <:ref:`proxy <ProxySettings>`>
+            <:ref:`cache <Cache>`>
+            <:ref:`cache_policy <CachePolicy>`>
+            <:ref:`terrain <TerrainOptions>`>
+
++------------------------+--------------------------------------------------------------------+
+| Property               | Description                                                        |
++========================+====================================================================+
+| lighting               | Whether to enable GL_LIGHTING on the entire map. By default this is|
+|                        | unset, meaning it will inherit the lighting mode of the scene.     |
++------------------------+--------------------------------------------------------------------+
+| elevation_interpolation| Algorithm to use when resampling elevation source data:            |
+|                        |   :nearest:     Nearest neighbor                                   |
+|                        |   :average:     Averages the neighoring values                     |
+|                        |   :bilinear:    Linear interpolation in both axes                  |
+|                        |   :triangulate: Interp follows triangle slope                      |
++------------------------+--------------------------------------------------------------------+
+| elevation_tile_size    | Forces the number of posts to render for each terrain tile. By     |
+|                        | default, the engine will use the size of the largest available     |
+|                        | source.                                                            |
++------------------------+--------------------------------------------------------------------+
+| overlay_texture_size   | Sets the texture size to use for draping (projective texturing)    |
++------------------------+--------------------------------------------------------------------+
+
+
+.. _TerrainOptions:
+
+Terrain Options
+~~~~~~~~~~~~~~~
+These options control the rendering of the terrain surface.
+
+.. parsed-literal::
+
+    <map>
+        <options>
+            <terrain driver                = "mp"
+                     lighting              = "true"
+                     sample_ratio          = "1.0"
+                     skirt_ratio           = "0.05"
+                     min_tile_range_factor = "6"
+                     min_lod               = "0"
+                     max_lod               = "23"
+                     first_lod             = "0"
+                     cluster_culling       = "true"
+                     mercator_fast_path    = "true"
+                     blending              = "false" >
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| driver                | Terrain engine plugin to load. Default = "mp".                     |
+|                       | Please refer to the driver reference guide for properties specific |
+|                       | to each individual plugin.                                         |
++-----------------------+--------------------------------------------------------------------+
+| lighting              | Whether to enable GL_LIGHTING on the terrain. By default this is   |
+|                       | unset, meaning it will inherit the lighting mode of the scene.     |
++-----------------------+--------------------------------------------------------------------+
+| sample_ratio          | Ratio of vertices per tile to the number of hieght samples in the  |
+|                       | source elevation data. Default = 1.0. You can reduce this number in|
+|                       | order to forcably downsample the terrain. *NOTE* that it is usually|
+|                       | better to set the ``elevation_tile_size`` property in the map      |
+|                       | options.                                                           |
++-----------------------+--------------------------------------------------------------------+
+| skirt_ratio           | Ratio of the height of a terrain tile "skirt" to the extent of the |
+|                       | tile. The *skirt* is geometry that hides gaps between adjacent     |
+|                       | tiles with different levels of detail.                             |
++-----------------------+--------------------------------------------------------------------+
+| min_tile_range_factor | Ratio of a tile's extent to its visibility range.                  |
++-----------------------+--------------------------------------------------------------------+
+| min_lod               | The lowest level of detail that the terrain is guaranteed to       |
+|                       | display, even if no source data is available at that LOD. The      |
+|                       | terrain will continue to subdivide up to this LOD even if it runs  |
+|                       | out of data.                                                       |
++-----------------------+--------------------------------------------------------------------+
+| max_lod               | The highest level of detail at which the terrain will render, even |
+|                       | if there is higher resolution source data available.               |
++-----------------------+--------------------------------------------------------------------+
+| first_lod             | The lowest level of detail at which the terrain will display tiles.|
+|                       | I.e., the terrain will never display a lower LOD than this.        |
++-----------------------+--------------------------------------------------------------------+
+| cluster_culling       | Disable "cluster culling" by setting this to ``false``. You may    |
+|                       | wish to do this is you are placing the camera underground.         |
++-----------------------+--------------------------------------------------------------------+
+| mercator_fast_path    | The *mercator fast path* allows the renderer to display Mercator   |
+|                       | projection imagery without reprojecting it. You can disable this   |
+|                       | technique (and allow reprojection as necessary) by setting this    |
+|                       | to ``false``.                                                      |
++-----------------------+--------------------------------------------------------------------+
+| blending              | Set this to ``true`` to enable GL blending on the terrain's        |
+|                       | underlying geometry. This lets you make the globe partially        |
+|                       | transparent. This is handy for seeing underground objects.         |
++-----------------------+--------------------------------------------------------------------+
+
+
+
+.. _ImageLayer:
+
+Image Layer
+~~~~~~~~~~~
+An *image layer* is a raster image overlaid on the map's geometry.
+
+.. parsed-literal::
+
+    <map>
+        <image name           = "my image layer"
+               driver         = "gdal"
+               nodata_image   = "http://readymap.org/nodata.png"
+               opacity        = "1.0"
+               min_range      = "0"
+               max_range      = "100000000"
+               min_level      = "0"
+               max_level      = "23"
+               min_resolution = "100.0"
+               max_resolution = "0.0"
+               enabled        = "true"
+               visible        = "true" >
+
+            <:ref:`cache_policy <CachePolicy>`>
+            <:ref:`color_filters <ColorFilterChain>`>
+            <:ref:`proxy <ProxySettings>`>
+
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| name                  | Readable layer name. Not used in the engine.                       |
++-----------------------+--------------------------------------------------------------------+
+| driver                | Plugin to use to create tiles for this layer.                      |
+|                       | Please refer to the driver reference guide for properties specific |
+|                       | to each individual plugin.                                         |
++-----------------------+--------------------------------------------------------------------+
+| nodata_image          | URI of an image that represents "no data" in the source. If        |
+|                       | osgEarth matches a tile to this image, it will act as if it found  |
+|                       | no data at that location and it will *not* render the tile.        |
++-----------------------+--------------------------------------------------------------------+
+| opacity               | The layer's opacity, [0..1].                                       |
++-----------------------+--------------------------------------------------------------------+
+| min_range             | Minimum visibility range, in meters from the camera. If the camera |
+|                       | gets closer than this, the tile will not be visible.               |
++-----------------------+--------------------------------------------------------------------+
+| max_range             | Maximum visibility range, in meters from the camera. The tile will |
+|                       | not be drawn beyond this range.                                    |
++-----------------------+--------------------------------------------------------------------+
+| min_level             | Minimum visibility level of detail.                                |
++-----------------------+--------------------------------------------------------------------+
+| max_level             | Maximum visibility level of detail.                                |
++-----------------------+--------------------------------------------------------------------+
+| min_resolution        | Minimum source data resolution at which to draw tiles. Value is    |
+|                       | units per pixel, in the native units of the source data.           |
++-----------------------+--------------------------------------------------------------------+
+| max_resolution        | Maximum source data resolution at which to draw tiles. Value is    |
+|                       | units per pixel, in the native units of the source data.           |
++-----------------------+--------------------------------------------------------------------+
+| enabled               | Whether to include this layer in the map. You can only set this at |
+|                       | load time; it is just an easy way of "commenting out" a layer in   |
+|                       | the earth file.                                                    |
++-----------------------+--------------------------------------------------------------------+
+| visible               | Whether to draw the layer.                                         |
++-----------------------+--------------------------------------------------------------------+
+
+
+.. _ElevationLayer:
+
+Elevation Layer
+~~~~~~~~~~~~~~~
+An *Elevation Layer* provides heightmap grids to the terrain engine. The osgEarth engine
+will composite all elevation data into a single heightmap and use that to build a terrain tile.
+
+.. parsed-literal::
+
+    <map>
+        <elevation name           = "text"
+                   driver         = "gdal"
+                   min_level      = "0"
+                   max_level      = "23"
+                   min_resolution = "100.0"
+                   max_resolution = "0.0"
+                   enabled        = "true" >
+
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| name                  | Readable layer name. Not used in the engine.                       |
++-----------------------+--------------------------------------------------------------------+
+| driver                | Plugin to use to create tiles for this layer.                      |
+|                       | Please refer to the driver reference guide for properties specific |
+|                       | to each individual plugin.                                         |
++-----------------------+--------------------------------------------------------------------+
+| min_level             | Minimum visibility level of detail.                                |
++-----------------------+--------------------------------------------------------------------+
+| max_level             | Maximum visibility level of detail.                                |
++-----------------------+--------------------------------------------------------------------+
+| min_resolution        | Minimum source data resolution at which to draw tiles. Value is    |
+|                       | units per pixel, in the native units of the source data.           |
++-----------------------+--------------------------------------------------------------------+
+| max_resolution        | Maximum source data resolution at which to draw tiles. Value is    |
+|                       | units per pixel, in the native units of the source data.           |
++-----------------------+--------------------------------------------------------------------+
+| enabled               | Whether to include this layer in the map. You can only set this at |
+|                       | load time; it is just an easy way of "commenting out" a layer in   |
+|                       | the earth file.                                                    |
++-----------------------+--------------------------------------------------------------------+
+
+
+.. _ModelLayer:
+
+Model Layer
+~~~~~~~~~~~
+A *Model Layer* renders non-terrain data, like vector features or external 3D models.
+
+.. parsed-literal::
+
+    <map>
+        <model name   = "my model layer"
+               driver = "feature_geom"
+
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| name                  | Readable layer name. Not used in the engine.                       |
++-----------------------+--------------------------------------------------------------------+
+| driver                | Plugin to use to create tiles for this layer.                      |
+|                       | Please refer to the driver reference guide for properties specific |
+|                       | to each individual plugin.                                         |
++-----------------------+--------------------------------------------------------------------+
+| enabled               | Whether to include this layer in the map. You can only set this at |
+|                       | load time; it is just an easy way of "commenting out" a layer in   |
+|                       | the earth file.                                                    |
++-----------------------+--------------------------------------------------------------------+
+| visible               | Whether to draw the layer.                                         |
++-----------------------+--------------------------------------------------------------------+
+
+
+
+.. _Profile:
+
+Profile
+~~~~~~~
+The profile tells osgEarth the spatial reference system, the geospatial extents, and the
+tiling scheme that it should use to render map tiles.
+
+.. parsed-literal::
+
+    <profile srs    = "+proj=utm +zone=17 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"
+             vdatum = "nad83"
+             xmin   = "560725.500"
+             xmax   = "573866.500"
+             ymin   = "4385762.500"
+             ymax   = "4400705.500"
+             num_tiles_wide_at_lod_0 = "1"
+             num_tiles_high_at_lod_0 = "1">
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| srs                   | Spatial reference system of the map. This can be a WKT string, an  |
+|                       | ESPG code, a PROJ4 initialization string, or a stock profile name. |
+|                       | Please refer to :doc:`/user/spatialreference` for details.         |
++-----------------------+--------------------------------------------------------------------+
+| vdatum                | Vertical datum of the profile, which describes how to treat        |
+|                       | Z values. Please refer to :doc:`/user/spatialreference` for        |
+|                       | details.                                                           |
++-----------------------+--------------------------------------------------------------------+
+| xmin, xmax, ymin, ymax| Geospatial extent of the map. The units are those defined by the   |
+|                       | SRS above (usually meters for a projected map, degrees for a       |
+|                       | geocentric map).                                                   |
++-----------------------+--------------------------------------------------------------------+
+| num_tiles_*_at_lod_0  | Size of the tile hierarchy's top-most level. Default is "1" in both|
+|                       | directions. (*optional*)                                           |
++-----------------------+--------------------------------------------------------------------+
+
+
+.. _Cache:
+
+Cache
+~~~~~
+Configures a cache for tile data.
+
+.. parsed-literal::
+
+    <cache driver = "filesystem"
+           path   = "c:/osgearth_cache" >
+
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| driver                | Plugin to use for caching.                                         |
+|                       | At the moment there is only one caching plugin that comes with     |
+|                       | osgEarth, the ``filesystem`` plugin.                               |
++-----------------------+--------------------------------------------------------------------+
+| path                  | Path (relative or absolute) or the root of a ``filesystem`` cache. |
++-----------------------+--------------------------------------------------------------------+
+
+
+.. _CachePolicy:
+
+CachePolicy
+~~~~~~~~~~~
+Policy that determines how a given element will interact with a configured cache.
+
+.. parsed-literal::
+
+    <cache_policy usage="no_cache">
+
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| usage                 | Policy towards the cache.                                          |
+|                       |   :read_write:  Use a cache if one is configured. The default.     |
+|                       |   :cache_only:  ONLY read data from the cache, ignoring the actual |
+|                       |                 data source. This is nice for offline rendering.   |
+|                       |   :no_cache:    Ignore caching and always read from the data       |
+|                       |                 source.                                            |
++-----------------------+--------------------------------------------------------------------+
+
+
+
+.. _ProxySettings:
+
+Proxy Settings
+~~~~~~~~~~~~~~
+*Proxy settings* let you configure a network proxy for remote data sources.
+
+.. parsed-literal::
+
+    <proxy host     = "hostname"
+           port     = "8080"
+           username = "jason"
+           password = "helloworld" >
+           
+Hopefully the properties are self-explanatory.
+
+
+
+.. _ColorFilterChain:
+
+Color Filters
+~~~~~~~~~~~~~
+A *color filter* is a pluggable shader that can alter the appearance of the
+color data in a layer before the osgEarth engine composites it into the terrain.
+
+.. parsed-literal::
+
+    <image>
+        <color_filters>
+            <gamma rgb="1.3">
+            ...
+            
+You can chain multiple color filters together. Please refer to :doc:`/references/colorfilters` for
+details on color filters.
diff --git a/docs/source/references/index.rst b/docs/source/references/index.rst
new file mode 100644
index 0000000..1e4e720
--- /dev/null
+++ b/docs/source/references/index.rst
@@ -0,0 +1,9 @@
+Reference Guides
+================
+
+.. toctree::
+   :maxdepth: 1
+   
+   earthfile
+   symbology
+   colorfilters
diff --git a/docs/source/references/symbology.rst b/docs/source/references/symbology.rst
new file mode 100644
index 0000000..a7c2459
--- /dev/null
+++ b/docs/source/references/symbology.rst
@@ -0,0 +1,383 @@
+Symbology Reference Guide
+=========================
+
+osgEarth renders *features* and *annotations* using *stylesheets*.
+This document lists all the symbol properties available for use in a
+stylesheet. Not every symbol is applicable to every situation; this
+is just a master list.
+
+Jump to a symbol:
+
+ * Geometry_
+ * Altitude_
+ * Extrusion_
+ * Icon_
+ * Model_
+ * Render_
+ * Skin_
+ * Text_
+ 
+**Developer Note**:
+
+    *In the SDK, symbols are in the ``osgEarth::Symbology`` namespace, and each
+    symbol class is in the form ``AltitudeSymbol`` for example. Properties below
+    are as they appear in the earth file; in the SDK, properties are available
+    via accessors in the form ``LineSymbol::strokeWidth()`` etc.
+ 
+
+Value Types
+-----------
+
+These are the basic value types. In the symbol tables on this page, each
+property includes the value type in parantheses following its description.
+
+  :float:                 Floating-point number
+  :float with units:      Floating-point number with unit designator, e.g.
+                          20px (20 pixels) or 10m (10 meters)
+  :HTML_Color:            Color string in hex format, as used in HTML; in the
+                          format #RRGGBB or #RRGGBBAA. (Example: #FFCC007F)
+  :integer:               Integral number
+  :numeric_expr:          Expression (simple or JavaScript) resolving to a number
+  :string:                Simple text string
+  :string_expr:           Expression (simple or JacaScript) resolving to a text string
+  :uri_string:            String denoting a resource location (like a URL or file path).
+                          URIs can be absolute or relative; relative URIs are always
+                          relative to the location of the *referrer*, i.e. the entity
+                          that requested the resource. (For example, a relative URI within
+                          an earth file will be relative to the location of the earth file
+                          itself.)
+
+                          
+Geometry
+--------
+
+Basic *geometry symbols* (SDK: ``LineSymbol``, ``PolygonSymbol``, ``PointSymbol``)
+control the color and style of the vector data.
+
++-----------------------+---------------------------------------+----------------------------+
+| Property              | Description                           | Value(s)                   |
++=======================+=======================================+============================+
+| fill                  | Fill color for a polygon.             | HTML color                 |
++-----------------------+---------------------------------------+----------------------------+
+| stroke                | Line color (or polygon outline color, | HTML color                 |
+|                       | if ``fill`` is present)               |                            |
++-----------------------+---------------------------------------+----------------------------+
+| stroke-width          | Line width                            | float with units           |
++-----------------------+---------------------------------------+----------------------------+
+| stroke-min-pixels     | Minimum rendering width; Prevents a   | float (pixels)             |
+|                       | line from getting thinner than this   |                            |
+|                       | value in pixels. Only applies when    |                            |
+|                       | the ``stroke-width`` is NOT in pixels |                            |
++-----------------------+---------------------------------------+----------------------------+
+| stroke-tessellation   | Number of times to subdivide a line   | integer                    |
++-----------------------+---------------------------------------+----------------------------+
+| stroke-linejoin       | Join style for polygonized lines.     | miter, round               |
+|                       | Only applies with ``stroke-width``    |                            |
+|                       | is in world units (and not pixels)    |                            |
++-----------------------+---------------------------------------+----------------------------+
+| stroke-linecap        | Cap style for polygonized lines.      | square, flat, round        |
+|                       | Only applies with ``stroke-width``    |                            |
+|                       | is in world units (and not pixels)    |                            |
++-----------------------+---------------------------------------+----------------------------+
+| stroke-rounding-ratio | For joins and caps that are set to    | float (0.4)                |
+|                       | ``round``, the resolution of the      |                            |
+|                       | rounded corner. Value is the ratio of |                            |
+|                       | line width to corner segment length.  |                            |
++-----------------------+---------------------------------------+----------------------------+
+| point-size            | Size for a GL point geometry          | float (1.0)                |
++-----------------------+---------------------------------------+----------------------------+
+
+
+Altitude
+--------
+
+The *altitude symbol* (SDK: ``AltitudeSymbol``) controls a feature's interaction with
+the terrain under its location.
+
++-----------------------+--------------------------------------------------------------------+
+| Property              | Description                                                        |
++=======================+====================================================================+
+| altitude-clamping     | Controls terrain following behavior.                               |
+|                       |   :none:     no clamping                                           |
+|                       |   :terrain:  clamp to terrain and discard Z values                 |
+|                       |   :relative: clamp to terrain and retain Z value                   |
+|                       |   :absolute: feature's Z contains its absolute Z.                  |
++-----------------------+--------------------------------------------------------------------+
+| altitude-technique    | When ``altitude-clamping`` is set to ``terrain``, chooses a        |
+|                       | terrain following technique:                                       |
+|                       |   :map:    clamp geometry to the map's elevation data tiles        |
+|                       |   :drape:  clamp geometry using a projective texture               |
+|                       |   :gpu:    clamp geometry to the terrain on the GPU                |
+|                       |   :scene:  re-clamp geometry to new paged tiles (annotations only) |
++-----------------------+--------------------------------------------------------------------+
+| altitude-binding      | Granulatiry at which to sample the terrain when                    |
+|                       | ``altitude-technique`` is ``map``:                                 |
+|                       |   :vertex:   clamp every vertex                                    |
+|                       |   :centroid: only clamp the centroid of each feature               |
++-----------------------+--------------------------------------------------------------------+
+| altitude-resolution   | Elevation data resolution at which to sample terrain height when   |
+|                       | ``altitude-technique`` is ``map`` (float)                          |
++-----------------------+--------------------------------------------------------------------+
+| altitude-offset       | Vertical offset to apply to geometry Z                             |
++-----------------------+--------------------------------------------------------------------+
+| altitude-scale        | Scale factor to apply to geometry Z                                |
++-----------------------+--------------------------------------------------------------------+
+
+
+Extrusion
+---------
+
+The *extrusion symbol* (SDK: ``ExtrusionSymbol``) directs osgEarth to create *extruded*
+geometry from the source vector data; Extrusion turns a 2D vector into a 3D shape.
+**Note:** The simple *presence* of an *extrusion* property will enable extrusion.
+
++-------------------------+--------------------------------------------------------------------+
+| Property                | Description                                                        |
++=========================+====================================================================+
+| extrusion-height        | How far to extrude the vector data (numeric-expr)                  |
++-------------------------+--------------------------------------------------------------------+
+| extrusion-flatten       | Whether to force all extruded vertices to the same Z value (bool). |
+|                         | For example, if you are extruding polygons to make 3D buildings,   |
+|                         | setting this to ``true`` will force the rooftops to be flat even   |
+|                         | if the underlying terrain is not. (boolean)                        |
++-------------------------+--------------------------------------------------------------------+
+| extrusion-wall-gradient | Factor by which to multiply the ``fill`` color of the extruded     |
+|                         | geometry at the *base* of the 3D shape. This results in the 3D     |
+|                         | shape being darker at the bottom than at the top, a nice effect.   |
+|                         | (float [0..1]; try 0.75)                                           |
++-------------------------+--------------------------------------------------------------------+
+| extrusion-wall-style    | Name of another style in the same stylesheet that osgEarth should  |
+|                         | apply to the *walls* of the extruded shape. (string)               |
++-------------------------+--------------------------------------------------------------------+
+| extrusion-roof-style    | Name of another style in the same stylesheet that osgEarth should  |
+|                         | apply to the *roof* of the extruded shape. (string)                |
++-------------------------+--------------------------------------------------------------------+
+
+
+Skin
+----
+
+The *skin symbol* (SDK: ``SkinSymbol``) applies texture mapping to a geometry, when applicable.
+(At the moment this only applies to *extruded* geometry.)
+
++-------------------------+--------------------------------------------------------------------+
+| Property                | Description                                                        |
++=========================+====================================================================+
+| skin-library            | Name of the *resource library* containing the skin(s)              |
++-------------------------+--------------------------------------------------------------------+
+| skin-tags               | Set of strings (separated by whitespace containing one or more     |
+|                         | *resource tags*. When selecting a texture skin to apply, osgEarth  |
+|                         | will limit the selection to skins with one of these tags. If you   |
+|                         | omit this property, all skins are considered. For example, if you  |
+|                         | are extruding buildings, you may only want to consider textures    |
+|                         | with the ``building`` tag. (string)                                |
++-------------------------+--------------------------------------------------------------------+
+| skin-tiled              | When set to ``true``, osgEarth will only consider selecting a skin |
+|                         | that has its ``tiled`` attribute set to ``true``. The ``tiled``    |
+|                         | attribute indicates that the skin may be used as a repeating       |
+|                         | texture. (boolean)                                                 |
++-------------------------+--------------------------------------------------------------------+
+| skin-object-height      | *Numeric expression* resolving to the feature's real-world height  |
+|                         | (in meters). osgEarth will use this value to narrow down the       |
+|                         | selection to skins appropriate to that height (i.e., skins for     |
+|                         | which the value falls between the skin's min/max object height     |
+|                         | range. (numeric-expr)                                              |
++-------------------------+--------------------------------------------------------------------+
+| skin-min-object-height  | Tells osgEarth to only consider skins whose minimum object height  |
+|                         | is greater than or equal to this value. (numeric-expr)             |
++-------------------------+--------------------------------------------------------------------+
+| skin-max-object-height  | Tells osgEarth to only consider skins whose maximum object height  |
+|                         | is less than or equal to this value. (numeric-expr)                |
++-------------------------+--------------------------------------------------------------------+
+| skin-random-seed        | Once the filtering is done (according to the properties above,     |
+|                         | osgEarth determines the minimal set of appropriate skins from      |
+|                         | which to choose and chooses one at random. By setting this seed    |
+|                         | value you can ensure that the same "random" selection happens each |
+|                         | time you run the appplication.  (integer)                          |
++-------------------------+--------------------------------------------------------------------+
+
+
+Icon
+----
+
+The *icon symbol* (SDK: ``IconSymbol``) describes the appearance of 2D icons.
+Icons are used for different things, the most common being:
+
+ * Point model substitution - replace geometry with icons
+ * Place annotations
+
++-------------------------+--------------------------------------------------------------------+
+| Property                | Description                                                        |
++=========================+====================================================================+
+| icon                    | URI of the icon image. (uri-string)                                |
++-------------------------+--------------------------------------------------------------------+
+| icon-library            | Name of a *resource library* containing the icon (optional)        |
++-------------------------+--------------------------------------------------------------------+
+| icon-placement          | For model substitution, describes how osgEarth should replace      |
+|                         | geometry with icons:                                               |
+|                         |    :vertex:   Replace each vertex in the geometry with an icon.    |
+|                         |    :interval: Place icons at regular intervals along the geometry, |
+|                         |               according to the ``icon-density`` property.          |
+|                         |    :random:   Place icons randomly within the geometry, according  |
+|                         |               to the ``icon-density`` property.                    |
+|                         |    :centroid: Place a single icon at the centroid of the geometry. |
++-------------------------+--------------------------------------------------------------------+
+| icon-density            | For ``icon-placement`` settings of ``interval`` or ``random``,     |
+|                         | this property is hint as to how many instances osgEarth should     |
+|                         | place. The unit is approximately "units per km" (for linear data)  |
+|                         | or "units per square km" for polygon data. (float)                 |
++-------------------------+--------------------------------------------------------------------+
+| icon-scale              | Scales the icon by this amount (float)                             |
++-------------------------+--------------------------------------------------------------------+
+| icon-heading            | Rotates the icon along its central axis (float, degrees)           |
++-------------------------+--------------------------------------------------------------------+
+| icon-declutter          | Activate *decluttering* for this icon. osgEarth will attempt to    |
+|                         | automatically show or hide things so they don't overlap on the     |
+|                         | screen. (boolean)                                                  |
++-------------------------+--------------------------------------------------------------------+
+| icon-align              | Sets the icon's location relative to its anchor point. The valid   |
+|                         | values are in the form "horizontal-vertical", and are:             |
+|                         |   * ``left-top``                                                   |
+|                         |   * ``left-center``                                                |
+|                         |   * ``left-bottom``                                                |
+|                         |   * ``center-top``                                                 |
+|                         |   * ``center-center``                                              |
+|                         |   * ``center-bottom``                                              |
+|                         |   * ``right-top``                                                  |
+|                         |   * ``right-center``                                               |
+|                         |   * ``right-bottom``                                               |
++-------------------------+--------------------------------------------------------------------+
+| icon-random-seed        | For random placement operations, set this seed so that the         |
+|                         | randomization is repeatable each time you run the app. (integer)   |
++-------------------------+--------------------------------------------------------------------+
+ 
+
+Model
+-----
+
+The *model symbol* (SDK: ``ModelSymbol``) describes external 3D models.
+Like icons, models are typically used for:
+
+ * Point model substitution - replace geometry with 3D models
+ * Model annotations
+
++-------------------------+--------------------------------------------------------------------+
+| Property                | Description                                                        |
++=========================+====================================================================+
+| model                   | URI of the 3D model (uri-string). Use this *OR* the                |
+|                         | ``model-library`` property, but not both.                          |
++-------------------------+--------------------------------------------------------------------+
+| model-library           | Name of a *resource library* containing the model. Use this *OR*   |
+|                         | the ``model`` property, but not both.                              |
++-------------------------+--------------------------------------------------------------------+
+| model-placement         | For model substitution, describes how osgEarth should replace      |
+|                         | geometry with models:                                              |
+|                         |    :vertex:   Replace each vertex in the geometry with a model.    |
+|                         |    :interval: Place models at regular intervals along the geometry,|
+|                         |               according to the ``model-density`` property.         |
+|                         |    :random:   Place models randomly within the geometry, according |
+|                         |               to the ``model-density`` property.                   |
+|                         |    :centroid: Place a single model at the centroid of the geometry.|
++-------------------------+--------------------------------------------------------------------+
+| model-density           | For ``model-placement`` settings of ``interval`` or ``random``,    |
+|                         | this property is hint as to how many instances osgEarth should     |
+|                         | place. The unit is approximately "units per km" (for linear data)  |
+|                         | or "units per square km" for polygon data. (float)                 |
++-------------------------+--------------------------------------------------------------------+
+| model-scale             | Scales the model by this amount along all axes (float)             |
++-------------------------+--------------------------------------------------------------------+
+| model-heading           | Rotates the about its +Z axis (float, degrees)                     |
++-------------------------+--------------------------------------------------------------------+
+| icon-random-seed        | For random placement operations, set this seed so that the         |
+|                         | randomization is repeatable each time you run the app. (integer)   |
++-------------------------+--------------------------------------------------------------------+
+ 
+ 
+Render
+------
+
+The *render symbol* (SDK: ``RenderSymbol``) applies general OpenGL rendering settings as well
+as some osgEarth-specific settings that are not specific to any other symbol type.
+
++-------------------------------+--------------------------------------------------------------+
+| Property                      | Description                                                  |
++===============================+==============================================================+
+| render-depth-test             | Enable or disable GL depth testing. (boolean)                |
++-------------------------------+--------------------------------------------------------------+
+| render-lighting               | Enable or disable GL lighting. (boolean)                     |
++-------------------------------+--------------------------------------------------------------+
+| render-depth-offset           | Enable or disable Depth Offseting. Depth offsetting is a     |
+|                               | GPU technique that modifies a fragment's depth value,        |
+|                               | simulating the rendering of that object closer or farther    |
+|                               | from the viewer than it actually is. It is a mechanism for   |
+|                               | mitigating z-fighting. (boolean)                             |
++-------------------------------+--------------------------------------------------------------+
+| render-depth-offset-min-bias  | Sets the minimum bias (distance-to-viewer offset) for depth  |
+|                               | offsetting. If is usually sufficient to set this property;   |
+|                               | all the others will be set automatically. (float, meters)    |
++-------------------------------+--------------------------------------------------------------+
+| render-depth-offset-max-bias  | Sets the minimum bias (distance-to-viewer offset) for depth  |
+|                               | offsetting.                                                  |
++-------------------------------+--------------------------------------------------------------+
+| render-depth-offset-min-range | Sets the range (distance from viewer) at which to apply the  |
+|                               | minimum depth offsetting bias. The bias graduates between its|
+|                               | min and max values over the specified range.                 |
++-------------------------------+--------------------------------------------------------------+
+| render-depth-offset-max-range | Sets the range (distance from viewer) at which to apply the  |
+|                               | maximum depth offsetting bias. The bias graduates between its|
+|                               | min and max values over the specified range.                 |
++-------------------------------+--------------------------------------------------------------+
+
+
+
+Text
+----
+
+The *text symbol* (SDK: ``TextSymbol``) controls the existance and appearance of text labels.
+
++-------------------------+--------------------------------------------------------------------+
+| Property                | Description                                                        |
++=========================+====================================================================+
+| fill                    | Foreground color of the text (HTML color)                          |
++-------------------------+--------------------------------------------------------------------+
+| text-size               | Size of the text (float, pixels)                                   |
++-------------------------+--------------------------------------------------------------------+
+| text-font               | Name of the font to use (system-dependent). For example, use       |
+|                         | "arialbd" on Windows for Arial Bold.                               |
++-------------------------+--------------------------------------------------------------------+
+| text-halo               | Outline color of the text; Omit this propery altogether for no     |
+|                         | outline. (HTML Color)                                              |
++-------------------------+--------------------------------------------------------------------+
+| text-halo-offset        | Outline thickness (float, pixels)                                  |
++-------------------------+--------------------------------------------------------------------+
+| text-align              | Alignment of the text string relative to its anchor point:         |
+|                         |   * ``left-top``                                                   |
+|                         |   * ``left-center``                                                |
+|                         |   * ``left-bottom``                                                |
+|                         |   * ``left-base-line``                                             |
+|                         |   * ``left-bottom-base-line``                                      |
+|                         |   * ``center-top``                                                 |
+|                         |   * ``center-center``                                              |
+|                         |   * ``center-bottom``                                              |
+|                         |   * ``center-base-line``                                           |
+|                         |   * ``center-bottom-base-line``                                    |
+|                         |   * ``right-top``                                                  |
+|                         |   * ``right-center``                                               |
+|                         |   * ``right-bottom``                                               |
+|                         |   * ``right-base-line``                                            |
+|                         |   * ``right-bottom-base-line``                                     |
+|                         |   * ``base-line``                                                  |
++-------------------------+--------------------------------------------------------------------+
+| text-content            | The actual text string to display (string-expr)                    |
++-------------------------+--------------------------------------------------------------------+
+| text-encoding           | Character encoding of the text content:                            |
+|                         |   * ``utf-8``                                                      |
+|                         |   * ``utf-16``                                                     |
+|                         |   * ``utf-32``                                                     |
+|                         |   * ``ascii``                                                      |
++-------------------------+--------------------------------------------------------------------+
+| text-declutter          | Activate *decluttering* for this icon. osgEarth will attempt to    |
+|                         | automatically show or hide things so they don't overlap on the     |
+|                         | screen. (boolean)                                                  |
++-------------------------+--------------------------------------------------------------------+
+
diff --git a/docs/source/releasenotes.rst b/docs/source/releasenotes.rst
new file mode 100644
index 0000000..6b75279
--- /dev/null
+++ b/docs/source/releasenotes.rst
@@ -0,0 +1,35 @@
+Release Notes
+=============
+
+Version 2.4 (April 2013)
+------------------------
+
+* New "MP" terrain engine with better performance and support for unlimited image layers (now the default)
+* Shader Composition - reworked the framework for more flexible control of vertex shaders
+* EarthManipulator - support for mobile (multitouch) actions
+* GPU clamping of feature geometry (ClampableNode)
+* TMSBackFiller tool to generate low-res LODs from high-res data
+* OceanSurface support for masking layer
+* New RenderSymbol for draw control
+* Fade-in control for feature layers
+* OverlayDecorator - improvements in draping; eliminated jittering
+* Added feature caching in FeatureSourceIndexNode
+* ShaderGenerator - added support for more texture types
+* Draping - moved draping/clamping control into Symbology (AltitudeSymbol)
+* Lines - add units to "stroke-width", for values like "25m", also "stroke-min-pixels"
+* PolygonizeLines operator with GPU auto-scaling
+* New Documentation site (stored in the repo) at http://osgearth.readthedocs.org
+* Decluttering - new "max_objects" property to limit number of drawables
+* New ElevationLOD node
+* SkyNode - added automatic ambient light calculation
+* New DataScanner - build ImageLayers from a recursive file search
+* Qt: new ViewWidget for use with a CompositeViewer
+* Map: batch updates using the beginUpdate/endUpdate construct
+* GLSL Color Filter: embed custom GLSL code directly in the earth file (glsl_filter.earth)
+* Agglite: Support for "stroke-width" with units and min-pixels for rasterization
+* Terrain options: force an elevation grid size with <elevation_tile_size>
+* Better iOS support
+* New "BYO" terrain engine lets you load an external model as your terrain
+* New "first_lod" property lets you force a minimum LOD to start at
+* Better support for tiled data layers
+* Lots of bug fixes and performance improvements
diff --git a/docs/source/startup.rst b/docs/source/startup.rst
new file mode 100644
index 0000000..bf1b525
--- /dev/null
+++ b/docs/source/startup.rst
@@ -0,0 +1,99 @@
+Building osgEarth
+=================
+
+osgEarth is a cross-platform library. It uses the CMake_ build system.
+You will need **version 2.8** or newer. 
+(This is the same build system that OpenSceneGraph_ uses.)
+
+NOTE: To build osgEarth for iOS see :doc:`ios`
+
+Get the Source Code
+-------------------
+
+**Option 1: use GIT**
+
+    osgEarth is hosted on GitHub_. You will nee da *git* client to access it.
+    We recommend TortoiseGit_ for Windows users.
+
+    To clone the repository, point your client at::
+
+        git://github.com/gwaldron/osgearth.git
+
+**Option 2: download a tagged version**
+
+    To download a tarball or ZIP archive of the source code, visit the
+    `osgEarth Tags`_ and select the one you want. The latest official release
+    will be at the top.
+
+Get the Dependencies
+--------------------
+
+The following are **required dependencies**:
+
+    * OpenSceneGraph_ 3.0.1 or later, with the CURL plugin enabled.
+    * GDAL_ 1.6 or later - Geospatial Data Abstraction Layer
+    * CURL_ - HTTP transfer library (comes with OpenSceneGraph_ 3rd party library distros)
+    
+These are the **optional depedencies**. osgEarth will compile without them,
+but some functionality will be missing:
+
+    * GEOS_ 3.2.0 or later - C++ library for topological operations.
+      osgEarth uses GEOS to perform various geometry operations like buffering and intersections.
+      If you plan to use vector feature data in osgEarth, you probably want this.
+    
+    * Minizip_ - ZIP file extractor; include this if you want to read KMZ files.
+    
+    * V8_ - Google's JavaScript engine. Include this if you want to embed JavaScript code
+      in your earth files.
+      
+**Optional: get pre-built dependencies**
+
+    * AlphaPixel_ has pre-built OSG_ and 3rd-party dependencies for various architectures.
+    * `Mike Weiblen`_ has some pre-built OSG_ binaries and dependencies too.
+    * FWTools_ has pre-built GDAL binaries with all the fixins.
+    * Pre-built `GDAL binaries` for various architectures.
+    
+Build it
+--------
+
+Make sure you built OSG_ and all the dependencies first.
+
+osgEarth uses CMake_, version 2.8 or later. Since OSG_ uses CMake_ as well, 
+once you get OSG built the process should be familiar.
+
+Here are a few tips.
+
+    * Always do an "out-of-source" build with CMake. That is, use a build directory
+      that is separate from the source code. This makes it easier to maintain separate
+      versions and to keep GIT updates clean.
+      
+    * For optional dependencies (like GEOS_ or V8_), just leave the CMake field blank
+      if you are not using it.
+      
+    * For the OSG dependencies, just input the **OSG_DIR** variable, and when you generate
+      CMake will automatically find all the other OSG directories.
+      
+    * As always, check `the forum`_ if you have problems!
+  
+**Good luck!!**
+
+----
+*Copyright Pelican Mapping Inc.*
+
+.. _GitHub:         http://github.com/gwaldron/osgearth
+.. _TortoiseGit:    http://code.google.com/p/tortoisegit
+.. _CMake:          http://www.cmake.org
+.. _osgEarth Tags:  http://github.com/gwaldron/osgearth/tags
+.. _OpenSceneGraph: http://openscenegraph.org
+.. _OSG:            http://openscenegraph.org
+.. _CURL:           http://curl.haxx.se/libcurl/
+.. _GEOS:           http://trac.osgeo.org/geos/ 
+.. _GDAL:           http://www.gdal.org/
+.. _GDAL binaries:  http://vbkto.dyndns.org:1280/sdk/Default.aspx
+.. _FWTools:        http://fwtools.maptools.org/
+.. _Minizip:        http://www.winimage.com/zLibDll/minizip.html
+.. _V8:             http://code.google.com/p/v8/
+.. _AlphaPixel:     http://openscenegraph.alphapixel.com/osg/downloads/openscenegraph-third-party-library-downloads
+.. _Mike Weiblen:   http://mew.cx/osg/
+.. _the forum:      http://forum.osgearth.org
+
diff --git a/docs/source/user/annotations.rst b/docs/source/user/annotations.rst
new file mode 100644
index 0000000..e69de29
diff --git a/docs/source/user/earthfiles.rst b/docs/source/user/earthfiles.rst
new file mode 100644
index 0000000..43cc2f6
--- /dev/null
+++ b/docs/source/user/earthfiles.rst
@@ -0,0 +1,149 @@
+Using Earth Files
+=================
+
+An *Earth File* is an XML description of a map. Creating an *earth file* is the
+easiest way to configure a map and get up and running quickly. In the osgEarth
+repository you will find dozens of sample earth files in the ``tests`` folder,
+covering various topics and demonstrating various features. We encourage you to
+explore and try them out!
+
+    Also see: :doc:`/references/earthfile`
+
+
+Contents of an Earth File
+-------------------------
+osgEarth uses an XML based file format called an *Earth File* to specify exactly
+how source data turns into an OSG scene graph. An Earth File has a ``.earth``
+extension, but it is XML.
+
+Fundamentally the Earth File allows you to specify:
+
+* The type of map to create (geocentric or projected)
+* The image, elevation, vector and model sources to use
+* Where the data will be cached
+
+
+A Simple Earth File
+-------------------
+Here is a very simple example that reads data from a GeoTIFF file on the local
+file system and renders it as a geocentric round Earth scene::
+
+    <map name="MyMap" type="geocentric" version="2">
+        <image name="bluemarble" driver="gdal">
+            <url>world.tif</url>
+        </image>
+    </map>
+
+This Earth File creates a geocentric Map named ``MyMap`` with a single
+GeoTIFF image source called ``bluemarble``. The ``driver`` attribute
+tells osgEarth which of its plugins to use to use to load the image.
+(osgEarth uses a plug-in framework to load different types of data
+from different sources.)
+
+Some of the sub-elements (under ``image``) are particular to the selected
+driver. To learn more about drivers and how to configure each one, please
+refer to the `Driver Reference Guide`_.
+
+    *Note: the ``version`` number is required!*
+
+
+Multiple Image Layers
+---------------------
+osgEarth supports maps with multiple image sources.
+This allows you to create maps such as base layer with a transportation
+overlay or provide high resolution insets for specific areas that sit
+atop a lower resolution base map.
+
+To add multiple images to a Earth File, simply add multiple "image" blocks
+to your Earth File::
+
+    <map name="Transportation" type="geocentric" version="2">
+    
+        <!--Add a base map of the blue marble data-->
+        <image name="bluemarble" driver="gdal">
+            <url>c:/data/bluemarble.tif</url>
+        </image>
+
+        <!--Add a high resolution inset of Washington, DC-->
+        <image name="dc" driver="gdal">
+            <url>c:/data/dc_high_res.tif</url>
+        </image>
+        
+    </map>
+
+The above map provides two images from local data sources using the GDAL driver.
+Order is important when defining multiple image sources: osgEarth renders them
+in the order in which they appear in the Earth File.
+
+    *Tip: relative paths within an Earth File are interpreted
+    as being relative to the Earth File itself.*
+
+
+Adding Elevation Data
+---------------------
+Adding elevation data (sometimes called "terrain data") to an Earth File is 
+very similar to adding images. Use an ``elevation`` block like so::
+
+    <map name="Elevation" type="geocentric" version="2">
+    
+        <!--Add a base map of the blue marble data-->
+        <image name="bluemarble" driver="gdal">
+            <url>c:/data/bluemarble.tif</url>
+        </image>
+
+        <!--Add SRTM data-->
+        <elevation name="srtm" driver="gdal">
+            <url>c:/data/SRTM.tif</url>
+        </elevation>
+        
+    </map>
+
+This Earth File has a base ``bluemarble`` image as well as a elevation
+grid that is loaded from a local GeoTIFF file. You can add as many elevation
+layers as you like; osgEarth will combine them into a single mesh.
+
+As with images, order is important - For example, if you have a base
+elevation data source with low-resolution coverage of the entire world and
+a high-resolution inset of a city, you need specify the base data FIRST,
+followed by the high-resolution inset.
+
+Some osgEarth drivers can generate elevation grids as well as imagery.
+
+    *Note: osgEarth only supports single-channel 16-bit integer or 32-bit
+    floating point data for use in elevation layers.*
+
+
+Caching
+-------
+Since osgEarth renders data on demand, it sometimes needs to do some work in
+order to prepare a tile for display. The *cache* exists so that osgEarth can
+save the results of this work for next time, instead of processing the tile
+anew each time. This increases performance and avoids multiple downloads of
+the same data. 
+
+Here's an example cache setup::
+
+    <map name="TMS Example" type="geocentric" version="2">
+    
+        <image name="metacarta blue marble" driver="tms">
+            <url>http://labs.metacarta.com/wms-c/Basic.py/1.0.0/satellite/</url>
+        </image>
+
+        <options>
+            <!--Specify where to cache the data-->
+            <cache type="filesystem">
+                <path>c:/osgearth_cache</path>
+            </cache>
+        </options>
+        
+    </map>
+    
+This Earth File shows the most basic way to specify a cache for osgEarth.
+This tells osgEarth to enable caching and to cache to the folder ``c:/osgearth_cache``.
+The cache path can be relative or absolute; relative paths are relative to the 
+Earth File itself.
+
+There are many ways to configure caching; please refer to the section on Caching_ for
+more details.
+
+.. _Driver Reference Guide:     #
diff --git a/docs/source/user/elevation.rst b/docs/source/user/elevation.rst
new file mode 100644
index 0000000..e69de29
diff --git a/docs/source/user/features.rst b/docs/source/user/features.rst
new file mode 100644
index 0000000..bda3900
--- /dev/null
+++ b/docs/source/user/features.rst
@@ -0,0 +1,420 @@
+Features & Symbology
+====================
+
+Understanding Features
+----------------------
+
+**Features** are vector geometry. Unlike imagery and elevation data (which are *rasters*),
+feature does not have a discrete display resolution. osgEarth can render features at any
+level of detail.
+
+A **Feature** is a combination of three components:
+
+  * Vector geometry (a collection of points, lines, or polygons)
+  * Attributes (a collection of name/value pairs)
+  * Spatial Reference (describing the geometry coordinates)
+
+Creating a Feature Layer
+------------------------
+
+osgEarth can render features in two different ways:
+
+  * Rasterized as an *image layer*
+  * Tessellated as a *model layer*
+
+Rasterization
+~~~~~~~~~~~~~
+
+*Rasterized features* are the simplest - osgEarth will "draw" the vectors to an image tile
+and then use that image tile in a normal image layer.
+
+osgEarth has one rasterizing feature driver: the ``agglite`` driver. Here's an example
+that renders an ESRI Shapefile as a rasterized image layer::
+
+    <model name="my layer" driver="agglite">
+        <features name="states" driver="ogr">
+            <url>states.shp</url>
+        </features>
+        <styles>
+            <style type="text/css">
+                states {
+                    stroke:       #ffff00;
+                    stroke-width: 2.0;
+                }
+            </style>
+        </styles>
+    </model>
+
+Tessellation
+~~~~~~~~~~~~
+
+*Tessellated features* go through a compilation process that turns the input vectors
+into OSG geometry (points, lines, triangles, or substituted 3D models). The primary
+feature tessellation plugin is the ``feature_geom`` driver - you will see this in use
+in most of osgEarth's earth files that demonstrate the use of feature data.
+
+Here is a *model layer* that renders an ESRI Shapefile as a series of yellow lines,
+rendered as OSG line geometry::
+
+    <model name="my layer" driver="feature_geom">
+        <features name="states" driver="ogr">
+            <url>states.shp</url>
+        </features>
+        <styles>
+            <style type="text/css">
+                states {
+                    stroke:       #ffff00;
+                    stroke-width: 2.0;
+                }
+            </style>
+        </styles>
+    </model>
+
+Components of a Feature Layer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As you can see from the examples above, there are a few necessary components 
+to any feature layer
+
+  * The ``<features>`` block describes the actual feature source; i.e., where osgEarth
+    should go to find the input data.
+    
+  * The ``<styles>`` block describes how osgEarth should render the features, i.e., 
+    their appearance in the scene. We call this the *stylesheet* or the *symbology*.
+    The makeup of the *stylesheet* can radically alter the appearance of the feature
+    data.
+  
+Both of these elements are required.
+
+Styling
+-------
+
+In an earth file, you may see a ``<styles>`` block that looks like this::
+
+    <styles>
+        <style type="text/css">
+            buildings {
+                altitude-clamping: terrain;
+                extrusion-height:  15;
+                extrusion-flatten: true;
+                fill:              #ff7f2f;
+            }    
+        </style>
+    </styles>
+    
+That is a *stylesheet* block. You will find this inside a ``<model>`` layer that is
+rendering feature data, paired with a ``<features>`` block. (The ``<features>`` block
+defines the source of the actual content.)
+
+In this case, the ``<style>`` element holds CSS-formatted data. A CSS style block can
+hold multiple styles, each of which has a name. In this case we only have one style:
+``buildings``. This style tells the geometry engine to do the following:
+
+    * Clamp the feature geometry to the terrain elevation data;
+    * Extrude shapes to a height of 15m above the terrain;
+    * Flatten the top of the extruded shape; and
+    * Color the shape orange.
+
+osgEarth takes a "model/view" approach to rendering features. It separates the
+concepts of *content* and *style*, much in the same way that a web application will
+use *CSS* to style the web content.
+
+osgEarth takes each input feature and subjects it to a styling process. The output
+will depend entirely on the combination of symbols in the stylesheet. This includes:
+
+  * Fill and Stroke - whether to draw the data as lines or polygons
+  * Extrusion - extruding 2D geometry into a 3D shape
+  * Substitution - replacing the geometry with external 3D models (e.g., trees) or icons
+  * Altitude - how the geometry interacts with the map's terrain
+  * Text - controls labeling
+  * Rendering - application of lighting, blending, and depth testing
+  
+Stylesheets
+~~~~~~~~~~~
+
+Each feature layer requires a *stylesheet*. The stylesheet appears as a ``<styles>``
+block in the earth file. Here's an example::
+
+    <model name="test" driver="feature_geom">
+        <features driver="ogr">
+            <geometry>POLYGON( (0 0, 1 0, 1 1, 0 1) )</geometry>
+            <profile>global-geodetic</profile>
+        </features>
+        <styles>
+            <style type="text/css">
+                default {
+                    fill:               #ff7f009f;
+                    stroke:             #ffffff;
+                    stroke-width:       2.0;
+                    altitude-clamping:  terrain;
+                    altitude-technique: drape;
+                    render-lighting:    false;
+                }
+            </style>
+        </styles>
+    </model>
+    
+The *stylesheet* contains one *style* called ``default``. Since there is only one
+style, osgEarth will apply it to all the input features. (To apply different styles 
+to different features, use *selectors* - more information below.)
+
+The style contains a set of *symbols* what describe how osgEarth should render
+the feature geometry. In this case:
+
+    :fill:   Draw a filled polygon in the specified HTML-style color (orange in this case).
+    :stroke: Outline the polygon in white.
+    :stroke-width: Draw the outline 2 pixels wide.
+    :altitude-clamping: Clamp the polygon to the terrain.
+    :altitude-technique: Use a "draping" technique to clamp the polygon (projective texturing).
+    :render-lighting: Disable OpenGL lighting on the polygon.
+
+This is only a small sample of available symbology.
+For a complete listing, please refer to: :doc:`/references/symbology`.
+
+
+Expressions
+~~~~~~~~~~~
+
+Some symbol properties support *expression*. An expression is a simple in-line calculation
+that uses feature attribute values to calculate a property dynamically.
+
+In an expression, you access a feature attribute value by enclosing its name
+in square brackets, like this: ``[name]``
+
+Example::
+
+    mystyle {
+        extrusion-height:  [hgt]*0.3048;           - read the "hgt" attribute, and convert it from feet to meters
+        altitude-offset:   max([base_offset], 1);  - use the greater of the "base_offset" attribute, and 1.0
+        text-content:      "Name: [name]";         - sets the text label to the concatenation of a literal and an attribute value 
+    }
+    
+The numeric expression evaluator supports basic arithmetic (+, -, *, / %), some utility
+functions (min, max), and grouping with parentheses. It also works for string values. 
+There are no operators, but you can still embed attributes.
+
+If simple expressions are not enough, you can embed JavaScript code -- please see the
+section on Scripting_ for more information.
+
+Style Selectors
+~~~~~~~~~~~~~~~
+
+TBD.
+
+Scripting
+~~~~~~~~~
+
+TBD.
+
+Terrain Following
+-----------------
+
+It is fairly common for features to interact with the terrain in some way.
+Requirements for this include things like:
+
+  * Streets that follow the contours of the terrain
+  * Trees planted on the ground
+  * Thematic mapping, like coloring a country's area based on its population
+  
+osgEarth offers a variety of terrain following approaches, because no single
+approach is best for every situation.
+
+
+Map Clamping
+~~~~~~~~~~~~
+
+*Map Clamping* is the simplest approach. When compiling the features for display,
+osgEarth will sample the *elevation layers* in the map, find the height of the terrian,
+and apply that to the resulting feature geometry. It will test each point along the
+geometry.
+
+Map clamping results in high-quality rendering; the trade-off is performance:
+
+    * It can be slow sampling the elevation data in the map, depending on the 
+      resolution you select. For a large number of features, it can be CPU-intensive
+      and time-consuming.
+    * Sampling is accurate, and done for every point in the geometry. You can opt to
+      sample at the *centroid* of each feature to improve compilation speed.
+    * Depending on the resolution of the feature geometry, you may need to tessellate
+      your data to achieve better quality.
+    * The rendering quality is good compared to other methods.
+    
+You can activate map clamping in your stylesheet like so::
+
+    altitude-clamping:   terrain;        // terrain-following on
+    altitude-technique:  map;            // clamp features to the map data
+    altitude-resolution: 0.005;          // [optional] resolution of map data to clamp to
+
+    
+Draping
+~~~~~~~
+
+*Draping* is the process of overlaying compiled geometry on the terrain skin, much
+like "draping" a blanket over an uneven surface. osgEarth does this be rendering the
+feature to a texture (RTT) and then projecting that texture down onto the terrain.
+
+Draping has its advantages and disadvantages:
+
+    * Draping will conform features perfectly to the terrain; there is no worrying
+      about resolution or tessellation.
+    * You may get jagged artificats when rendering lines or polygon edges. 
+      The projected texture is of limited size, and the larger of an area it must
+      cover, the lower the resolution of the image being projected. This means
+      that in practice draping is more useful for polygons than for lines.
+    * Unexpected blending artifacts may result from draping many transparent
+      geometries atop each other.
+      
+You can activate draping like so::
+
+    altitude-clamping:   terrain;        // terrain-following on
+    altitude-technique:  drape;          // drape features with a projective texture
+    
+    
+GPU Clamping
+~~~~~~~~~~~~
+
+*GPU Clamping* implements approximate terrain following using GPU shaders. It uses
+a two-phase technique: first it uses depth field sampling to clamp each vertex to
+the terrain skin in a vertex shader; secondly it applies a depth-offsetting 
+algorithm in the fragment shader to mitigate z-fighting.
+
+GPU clamping also has its trade-offs:
+
+    * It is very well suited to lines (or even triangulated lines), but less so
+      to polygons because it needs to tessellate the interior of a polygon in order
+      to do a good approximate clamping.
+    * It is fast, happens completely at runtime, and takes advantage of the GPU's
+      parallel processing.
+    * There are no jagged-edge effects as there are in draping.
+    
+Set up GPU clamping like this::
+
+    altitude-clamping:   terrain;        // terrain-following on
+    altitude-technique:  gpu;            // clamp and offset feature data on the GPU
+    
+
+Rendering Large Datasets
+------------------------
+
+The simplest way to load feature data into osgEarth is like this::
+
+   <model name="shapes">
+      <features name="data" driver="ogr">
+         <url>data.shp</url>
+      </features>
+      <styles>
+         data {
+             fill: #ffff00;
+         }
+      </styles>
+   </model>
+
+We just loaded every feature in the shapefile and colored them all yellow.
+
+This works fine up to a point -- the point at which osgEarth (and OSG) become
+overloaded with too much geometry. Even with the optimizations that osgEarth's
+geometry compiler employs, a large enough dataset can exhaust system resources.
+
+The solution to that is feature tiling and paging. Here is how to configure it.
+
+Feature display layouts
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The feature display layout activates paging and tiles of feature data.
+Let's modify the previous example::
+
+   <model name="shapes">
+      <features name="data" driver="ogr">
+         <url>data.shp</url>
+      </features>
+
+      <layout>
+          <tile_size_factor>15.0</tile_size_factor>
+          <level name="only" max_range="100000"/>
+      </layout>
+
+      <styles>
+         data {
+             fill: #ffff00;
+         }
+      </styles>
+   </model>
+   
+The mere presence of the ``<layout>`` element activates paging. This means that
+instead of being loaded and compiled at load time, the feature data will load
+and compile in the background once the application has started. There may be a
+delay before the feature data shows up in the scene, depending on its complexity.
+
+The presence of ``<level>`` elements within the layout actives tiling and levels of
+detail. If you OMIT levels, the data will still load in the background, but it will
+all load at once. With one or more levels, osgEarth will break up the feature data
+into tiles at one or more levels of detail and page those tiles in individually.
+More below.
+
+Levels
+~~~~~~
+
+Each level describes a level of detail. This is a camera range (between ``min_range``
+and ``max_range``) at which tiles in this level of detail are rendered. But how
+big is each tile? This is calculated based on the *tile range factor*.
+
+The ``tile_range_factor`` determines the size of a tile, based on the ``max_range``
+of the LOD. The tile range factor is the multiple of a tile's radius at which the
+LOD's ``max_range`` takes effect. In other words::
+
+    tile radius = max range / tile size factor.
+    
+The default tile range factor is **15.0**.
+
+The upshot is: if you want larger tiles, reduce the range factor.
+For smaller tiles, increase the factor.
+
+Why do you care about tile size? Because the density of your data will affect
+how much geometry is in each tile. And since OSG (OpenGL, really) benefits from
+sending large batches of similar geometry to the graphics card, tweaking the
+tile size can help with performance and throughput. Unfortunately there's no way
+for osgEarth to know exactly what the "best" tile size will be in advance;
+so, you have the opportunity to tweak using this setting.
+
+Multiple Levels and Using Selectors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can have any number of levels -- but keep in mind that unlike the terrain
+imagery, feature data does NOT form a quadtree in which the higher LODs replace
+the lower ones. Rather, feature levels are independent of one another. So if you
+want to draw more detail as you zoom in closer, you have to use selectors to
+decide what you want to draw at each step.
+
+Here's an example. Say we're drawing roads.
+We have a shapefile in which the road type is stored in an attribute called ``roadtype`` ::
+
+   <layout>
+       <tile_size_factor>15.0</tile_size_factor>
+       <crop_features>true</crop_features>
+       <level name="highway" max_range="100000">
+          <selector class="highway">
+             <query>
+                <expr>roadtype = 'A'</expr>
+             </query>
+          </selector>
+       </level>
+       <level name="street" max_range="10000">
+          <selector class="street">
+             <query>
+                <expr>roadtype = 'B'</expr>
+             </query>
+          </selector>
+       </level>
+   </layout>
+
+   <styles>
+      highway {
+          stroke:       #ffff00;
+          stroke-width: 2.0;
+      }
+      street {
+          stroke:       #ffffff7f;
+          stroke-width: 1.0;
+      }
+   </styles>
+   
diff --git a/docs/source/user/imagery.rst b/docs/source/user/imagery.rst
new file mode 100644
index 0000000..77bc25c
--- /dev/null
+++ b/docs/source/user/imagery.rst
@@ -0,0 +1,2 @@
+Imagery
+=======
diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst
new file mode 100644
index 0000000..7e90ee7
--- /dev/null
+++ b/docs/source/user/index.rst
@@ -0,0 +1,15 @@
+User Guide
+==========
+
+.. toctree::
+   :maxdepth: 2
+   
+   earthfiles
+   maps
+   spatialreference
+   profiles
+   imagery
+   elevation
+   features
+   symbology
+   annotations
diff --git a/docs/source/user/maps.rst b/docs/source/user/maps.rst
new file mode 100644
index 0000000..e69de29
diff --git a/docs/source/user/profile.rst b/docs/source/user/profile.rst
new file mode 100644
index 0000000..e69de29
diff --git a/docs/source/user/spatialreference.rst b/docs/source/user/spatialreference.rst
new file mode 100644
index 0000000..d31ac1c
--- /dev/null
+++ b/docs/source/user/spatialreference.rst
@@ -0,0 +1,256 @@
+Spatial References
+==================
+
+We specify locations on the Earth using *coordinates*, tuples of numbers that
+pinpoint a particular place on the map at some level of precision. But just 
+knowing the coordinates is not enough; you need to know how to interpret them.
+
+  A **Spatial Reference** (SRS) maps a set of coordinates
+  to a corresponding real location on the earth.
+  
+For example, given the coordinates of a location on the earth::
+
+  (-121.5, 36.8, 2000.0)
+  
+Those numbers are meaningless unless you know how to use them.
+So combine that with some reference information::
+
+  Coordinate System Type: Geographic
+  Units:                  Degrees
+  Horizontal datum:       WGS84
+  Vertical datum:         EGM96
+
+Now you can figure out exactly where the point is on earth, where it is relative to
+other points, and how to convert it to other representations.
+
+Components of an SRS
+--------------------
+
+A *spatial reference*, or *SRS*, contains:
+
+* `Coordinate System Type`_
+* `Horizontal Datum`_
+* `Vertical Datum`_
+* `Projection`_
+
+Coordinate System Type
+~~~~~~~~~~~~~~~~~~~~~~
+
+osgEarth supports three basic coordinate system types:
+
+* **Geographic** - A whole-earth, ellipsoidal model. Coordinates are spherical angles
+  in *degrees* (longitude and latitude). Examples include WGS84 and NAD83.
+  (`Learn more <http://en.wikipedia.org/wiki/Geographic_coordinate_system>`_)
+  
+* **Projected** - A local coordinate system takes a limited region of the earth and
+  "projects" it into a 2D cartesion (X,Y) plane. Examples include UTM, US State Plane,
+  and Mercator.
+  (`Learn more <http://en.wikipedia.org/wiki/Map_projection>`_.)
+  
+* **ECEF** - A whole earth, cartesian system. ECEF = Earth Centered Earth Fixed; it is
+  a 3D cartesion system (X,Y,Z) with the origin (0,0,0) at the earth's center; the X-axis
+  intersecting lat/long (0,0), the Y-axis intersecting lat/long (0,-90), and the Z-axis
+  intersecting the north pole. ECEF is the native system in which osgEarth renders its
+  graphics. (`Learn more <http://en.wikipedia.org/wiki/ECEF>`_)
+
+Horizontal Datum
+~~~~~~~~~~~~~~~~
+
+A *datum* is a reference point (or set of points) against which geospatial 
+measurements are made. The same location on earth can have different coordinates
+depending on which datum is in use. There are two classes of datum:
+
+  A **horizontal datum** measures positions on the earth. Since the earth is not
+  a perfect sphere or even a perfect ellipsoid, particular datums are usually
+  designed to approximate the shape of the earth in a particular region.
+  Common datums include **WGS84** and **NAD83** in North America, and **ETR89**
+  in Europe. 
+  
+Vertical Datum
+~~~~~~~~~~~~~~
+  
+A **vertical datum** measures elevation. There are several classes of vertical
+datum; osgEarth supports *geodetic* (based on an ellipsoid) and *geoid* (based
+on a sample set of elevation points around the planet).
+
+osgEarth has the following vertical datums built in:
+
+* Geodetic - the default; osgEarth uses the Horizontal datum ellipsoid as a reference
+* EGM84 geoid
+* EGM96 geoid - commonly called *MSL*; used in DTED and KML
+* EGM2008 geoid
+
+By default, SRS's in osgEarth use a *geodetic* vertical datum; i.e., altitude is 
+measured as "height above ellipsoid (HAE)".
+
+Projection
+~~~~~~~~~~
+
+A *projected* SRS will also have a *Projection*. This is a mathematical formula 
+for transforming a point on the ellipsoid into a 2D plane (and back).
+
+osgEarth supports thousands of known projections (by way of the GDAL/OGR toolkit).
+Notable ones include:
+
+* UTM (Universal Transverse Mercator)
+* Sterographic
+* LCC (Lambert Conformal Conic)
+
+Each has particular characteristics that makes it desirable for certain types of
+applications. Please see `Map Projections`_ on Wikipedia to learn more.
+
+.. _Map Projections:       http://en.wikipedia.org/wiki/Map_projection
+
+
+SRS Representations
+-------------------
+
+There are many ways to define an SRS. osgEarth supports the following.
+
+WKT (Well Known Text)
+~~~~~~~~~~~~~~~~~~~~~
+
+WKT is an OGC standard for describing a coordinate system. It is commonly 
+found in a ".prj" file alongside a piece of geospatial data, like a shapefile
+or an image.
+
+Here is the WKT representation for the *UTM Zone 15N* projection::
+
+    PROJCS["NAD_1983_UTM_Zone_15N",
+        GEOGCS["GCS_North_American_1983",
+            DATUM["D_North_American_1983",
+                SPHEROID["GRS_1980",6378137.0,298.257222101]],
+            PRIMEM["Greenwich",0.0],
+            UNIT["Degree",0.0174532925199433]],
+        PROJECTION["Transverse_Mercator"],
+        PARAMETER["False_Easting",500000.0],
+        PARAMETER["False_Northing",0.0],
+        PARAMETER["Central_Meridian",-93.0],
+        PARAMETER["Scale_Factor",0.9996],
+        PARAMETER["Latitude_Of_Origin",0.0],
+        UNIT["Meter",1.0]]
+        
+PROJ4
+~~~~~
+
+*PROJ4* is a map projections toolkit used by osgEarth and hundreds of other 
+geospatial applications and toolkits. It has a shorthand represtation for 
+describing an SRS. Here is the same SRS above, this time in PROJ4 format::
+
+    +proj=utm +zone=15 +ellps=GRS80 +units=m +no_defs
+    
+PROJ4 has data tables for all the common components (like UTM zones and datums)
+so you don't have to explicitly define everything like you do with WKT.
+
+EPSG Codes
+~~~~~~~~~~
+
+The EPSG (the now-defunct European Petroleum Survey Group) established a table
+of numerical codes for referencing well-known projections. You can browse a list
+of there `here <http://spatialreference.org/ref/epsg>`_. osgEarth will accept
+EPSG codes; again for the example above::
+
+    epsg:26915
+    
+If you know the EPSG code it's a nice shorthand way to express it. OGR/PROJ4,
+which osgEarth requires, includes a large table of EPSG codes.
+
+Aliases
+~~~~~~~
+
+The last category is the *named SRS*. There are some SRS's that are so common
+that we include shorthand notation for them. These include:
+
+    :wgs84:              World Geographic Survey 1984 geographic system
+    :spherical-mercator: Spherical mercator (commonly used in web mapping systems)
+    :plate-carre:        WGS84 projected flat (X=longitude, Y=latitude)
+
+    
+    
+
+Using Spatial References in osgEarth
+------------------------------------
+
+There are several ways to work with an SRS in osgEarth, but the easiest way it
+to use the ``GeoPoint`` class. However let's look at creating an SRS first and
+then move on to the class.
+
+SpatialReference API
+~~~~~~~~~~~~~~~~~~~~
+
+The ``SpatialReference`` class represents an SRS. Lots of classes and functions in
+osgEarth require an SRS. Here's how you create on in code::
+
+    const SpatialReference* srs = SpatialReference::get("epsg:4326");
+    
+That will give you an SRS. The ``get()`` function will accept any of the SRS
+representations we discussed above: WKT, PROJ4, EPSG, or Aliases.
+
+If you need an SRS with a vertical datum, express that as a second parameter::
+
+    srs = SpatialReference::get("epsg:4326", "egm96");
+
+It's sometimes useful to be able to access an SRS's component types as well. For
+example, every *projected* SRS has a base *geographic* SRS that it's based upon.
+You can get this by calling::
+
+    geoSRS = srs->getGeographicSRS();
+
+If you're transforming a projected point to latitude/longitude, that's the output
+SRS you will want.
+
+You can also grab an ECEF SRS corresponding to any SRS, like so::
+
+    ecefSRS = srs->getECEF();    
+
+``SpatialReference`` has lots of functions for doing transformations, etc. Consult 
+the header file for information on those. But in practice it is usually best to use
+classes like ``GeoPoint`` instead of using ``SpatialReference`` directly.
+
+
+GeoPoint API
+~~~~~~~~~~~~
+
+A ``GeoPoint`` is a georeferenced 2D or 3D point. ("Georeferenced" means that the
+coordinate values are paired with an SRS - this means all the information necessary
+to plot that point on the map is self-contained.) There are other "Geo" classes
+including ``GeoExtent`` (a bounding box) and ``GeoCircle`` (a bounding circle).
+
+Here is how you create a 2D ``GeoPoint``::
+
+    GeoPoint point(srs, x, y);
+    
+You can also create a 3D ``GeoPoint`` with an altitude::
+
+    GeoPoint point(srs, x, y, z, ALTMODE_ABSOLUTE);
+    
+The ``ALTMODE_ABSOLUTE`` is the *altitude mode*, and it required when you specify
+a 3D coordinate:
+
+    :``ALTMODE_ABSOLUTE``:  Z is relative to the SRS' vertical datum, i.e., 
+                            height above ellipsoid or height above the geoid.
+    :``ALTMODE_RELATIVE``:  Z is relative to the height of the terrain under
+                            the point.
+
+Now that you have your ``GeoPoint`` you can do transformations on it. Say you 
+want to transform it to another SRS::
+
+    GeoPoint point(srs, x, y);
+    GeoPoint newPoint = point.transform(newSRS);
+    
+Here's a more concrete example. Say you have a point in latitude/longitude (WGS84)
+and you need to express it in UTM Zone 15N::
+
+    const SpatialReference* wgs84 = SpatialReference::get("wgs84");
+    const SpatialReference* utm15 = SpatialReference::get("+proj=utm +zone=15 +ellps=GRS80 +units=m");
+    ...
+    GeoPoint wgsPoint( wgs84, -93.0, 34.0 );
+    GeoPoint utmPoint = wgsPoint.transform( utm15 );
+    
+    if ( utmPoint.isValid() )
+       // do something
+       
+Always check ``isValid()`` because not every point in one SRS can be transformed
+into another SRS. UTM Zone 15, for example, is only defined for a 6-degree span
+of longitude -- values too far outside this range might fail!
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 07c49da..2ee8c2b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,7 +13,10 @@ FOREACH( lib
 ENDFOREACH( lib )
 
 ADD_SUBDIRECTORY( osgEarthDrivers )
+
+IF(NOT OSG_BUILD_PLATFORM_IPHONE AND NOT OSG_BUILD_PLATFORM_IPHONE_SIMULATOR AND NOT ANDROID)
 ADD_SUBDIRECTORY( applications )
+ENDIF()
 
 IF (QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT)
     ADD_SUBDIRECTORY(osgEarthQt)
diff --git a/src/applications/CMakeLists.txt b/src/applications/CMakeLists.txt
index f82b456..058aa01 100644
--- a/src/applications/CMakeLists.txt
+++ b/src/applications/CMakeLists.txt
@@ -33,8 +33,12 @@ ADD_SUBDIRECTORY(osgearth_featureinfo)
 ADD_SUBDIRECTORY(osgearth_package)
 ADD_SUBDIRECTORY(osgearth_tfs)
 ADD_SUBDIRECTORY(osgearth_boundarygen)
+ADD_SUBDIRECTORY(osgearth_backfill)
 ADD_SUBDIRECTORY(osgearth_overlayviewer)
 ADD_SUBDIRECTORY(osgearth_version)
+IF (QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT)
+    ADD_SUBDIRECTORY(osgearth_package_qt)
+ENDIF()
 
 
 SET(TARGET_DEFAULT_LABEL_PREFIX "Sample")
@@ -68,6 +72,8 @@ ADD_SUBDIRECTORY(osgearth_occlusionculling)
 ADD_SUBDIRECTORY(osgearth_colorfilter)
 ADD_SUBDIRECTORY(osgearth_contour)
 ADD_SUBDIRECTORY(osgearth_verticalscale)
+ADD_SUBDIRECTORY(osgearth_sequencecontrol)
+ADD_SUBDIRECTORY(osgearth_minimap)
 
 IF(NOT ${OPENSCENEGRAPH_VERSION} VERSION_LESS "3.1.0")
     ADD_SUBDIRECTORY(osgearth_shadow)
@@ -76,4 +82,7 @@ ENDIF()
 IF (QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT)
     ADD_SUBDIRECTORY(osgearth_qt)
     ADD_SUBDIRECTORY(osgearth_qt_simple)
+    ADD_SUBDIRECTORY(osgearth_qt_windows)
 ENDIF()
+
+#ADD_SUBDIRECTORY(osgearth_silverlining)
diff --git a/src/applications/osgearth_annotation/osgearth_annotation.cpp b/src/applications/osgearth_annotation/osgearth_annotation.cpp
index aeaf26d..b29d071 100644
--- a/src/applications/osgearth_annotation/osgearth_annotation.cpp
+++ b/src/applications/osgearth_annotation/osgearth_annotation.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -217,6 +217,7 @@ main(int argc, char** argv)
         geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Cyan;
         geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 5.0f;
         geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
         FeatureNode* gnode = new FeatureNode(mapNode, new Feature(geom, geoSRS, geomStyle));
         annoGroup->addChild( gnode );
 
@@ -236,6 +237,7 @@ main(int argc, char** argv)
         geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Lime;
         geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 3.0f;
         geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
         FeatureNode* gnode = new FeatureNode(mapNode, new Feature(geom, geoSRS, geomStyle));
         annoGroup->addChild( gnode );
 
@@ -254,6 +256,7 @@ main(int argc, char** argv)
         pathStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Red;
         pathStyle.getOrCreate<LineSymbol>()->stroke()->width() = 3.0f;
         pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+        pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
 
         Feature* pathFeature = new Feature(path, geoSRS, pathStyle);
         pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE;
@@ -272,16 +275,39 @@ main(int argc, char** argv)
     {
         Style circleStyle;
         circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Cyan, 0.5);
+        //circleStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color(Color::Cyan, 1.0);
+        //circleStyle.getOrCreate<LineSymbol>()->stroke()->width() = 6.0f;
+        //circleStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
+        circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+        circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
+
         CircleNode* circle = new CircleNode(
-            mapNode, 
+            mapNode,
             GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
-            Linear(300, Units::KILOMETERS ),
-            circleStyle,
-            false );
-        annoGroup->addChild( circle );        
+            Linear(300, Units::KILOMETERS),
+            circleStyle, Angular(-45.0, Units::DEGREES), Angular(45.0, Units::DEGREES), true);
+        annoGroup->addChild( circle );
 
         editorGroup->addChild( new CircleNodeEditor( circle ) );
     }
+	{
+		Style circleStyle;
+		circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Red, 0.5);
+		//circleStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color(Color::Red, 1.0);
+		//circleStyle.getOrCreate<LineSymbol>()->stroke()->width() = 6.0f;
+		//circleStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
+		circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+		circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
+
+		CircleNode* circle = new CircleNode(
+			mapNode,
+			GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
+			Linear(300, Units::KILOMETERS),
+			circleStyle, Angular(45.0, Units::DEGREES), Angular(360.0 - 45.0, Units::DEGREES), true);
+		annoGroup->addChild( circle );
+
+		editorGroup->addChild( new CircleNodeEditor( circle ) );
+	}
 
     //--------------------------------------------------------------------
 
@@ -289,29 +315,48 @@ main(int argc, char** argv)
     {
         Style ellipseStyle;
         ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Orange, 0.75);
+        //ellipseStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color(Color::Orange, 0.75);
+        //ellipseStyle.getOrCreate<LineSymbol>()->stroke()->width() = 4.0f;
+        ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
         EllipseNode* ellipse = new EllipseNode(
             mapNode, 
             GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
             Linear(500, Units::MILES),
             Linear(100, Units::MILES),
             Angular(0, Units::DEGREES),
-            ellipseStyle,
-            true );
+            ellipseStyle, Angular(45.0, Units::DEGREES), Angular(360.0 - 45.0, Units::DEGREES), true);
         annoGroup->addChild( ellipse );
         editorGroup->addChild( new EllipseNodeEditor( ellipse ) );
     }
+	{
+		Style ellipseStyle;
+		ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Blue, 0.75);
+		//ellipseStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color(Color::Blue, 0.75);
+		//ellipseStyle.getOrCreate<LineSymbol>()->stroke()->width() = 4.0f;
+		ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
+		EllipseNode* ellipse = new EllipseNode(
+			mapNode, 
+			GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
+			Linear(500, Units::MILES),
+			Linear(100, Units::MILES),
+			Angular(0, Units::DEGREES),
+			ellipseStyle, Angular(-45.0, Units::DEGREES), Angular(45.0, Units::DEGREES), true);
+		annoGroup->addChild( ellipse );
+		editorGroup->addChild( new EllipseNodeEditor( ellipse ) );
+	}
 
     {
         // A rectangle around San Diego
         Style rectStyle;
         rectStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Green, 0.5);
+        rectStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+        rectStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
         RectangleNode* rect = new RectangleNode(
             mapNode, 
             GeoPoint(geoSRS, -117.172, 32.721),
             Linear(300, Units::KILOMETERS ),
             Linear(600, Units::KILOMETERS ),
-            rectStyle,
-            true );
+            rectStyle);
         annoGroup->addChild( rect );
 
         editorGroup->addChild( new RectangleNodeEditor( rect ) );
diff --git a/src/applications/osgearth_backfill/CMakeLists.txt b/src/applications/osgearth_backfill/CMakeLists.txt
new file mode 100644
index 0000000..2f493ec
--- /dev/null
+++ b/src/applications/osgearth_backfill/CMakeLists.txt
@@ -0,0 +1,7 @@
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
+SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
+
+SET(TARGET_SRC osgearth_backfill.cpp )
+
+#### end var setup  ###
+SETUP_APPLICATION(osgearth_backfill)
\ No newline at end of file
diff --git a/src/applications/osgearth_backfill/osgearth_backfill.cpp b/src/applications/osgearth_backfill/osgearth_backfill.cpp
new file mode 100644
index 0000000..28ad8ac
--- /dev/null
+++ b/src/applications/osgearth_backfill/osgearth_backfill.cpp
@@ -0,0 +1,133 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 <osg/io_utils>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/WriteFile>
+
+#include <osgEarth/Common>
+#include <osgEarth/FileUtils>
+#include <osgEarth/ImageUtils>
+#include <osgEarth/Registry>
+#include <osgEarth/StringUtils>
+#include <osgEarthUtil/TMS>
+#include <osgEarthUtil/TMSBackFiller>
+
+
+#include <iostream>
+#include <sstream>
+
+using namespace osgEarth;
+using namespace osgEarth::Util;
+using namespace osgEarth::Util::TMS;
+
+/** Prints an error message, usage information, and returns -1. */
+int
+usage( const std::string& msg = "" )
+{
+    if ( !msg.empty() )
+    {
+        std::cout << msg << std::endl;
+    }
+
+    std::cout
+        << std::endl
+        << "USAGE: osgearth_backfill <tms.xml>" << std::endl
+        << std::endl        
+        << "            --bounds xmin ymin xmax ymax    : bounds to backfill in (in map coordinates; default=entire map)\n"
+        << "            [--min-level <num>]             : The minimum level to stop backfilling to.  (default=0)\n"
+        << "            [--max-level <num>]             : The level to start backfilling from(default=inf)\n"                
+        << "            [--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;
+
+    return -1;
+}
+
+
+/** Prints a message and returns a non-error return code. */
+int
+message( const std::string& msg )
+{
+    if ( !msg.empty() )
+    {
+        std::cout << msg << std::endl << std::endl;
+    }
+    return 0;
+}
+
+int
+main(int argc, char** argv)
+{
+    osg::ArgumentParser args(&argc,argv);    
+
+    // verbosity?
+    bool verbose = !args.read( "--quiet" );
+     
+    Bounds bounds;
+    // restrict user-specified bounds.    
+    double xmin=DBL_MAX, ymin=DBL_MAX, xmax=DBL_MIN, ymax=DBL_MIN;
+    while (args.read("--bounds", xmin, ymin, xmax, ymax ))
+    {                
+        bounds.set( xmin, ymin, 0, xmax, ymax, 1 );        
+    }        
+
+    // min level to backfill to
+    unsigned minLevel = 0;
+    args.read( "--min-level", minLevel );
+
+    // max level to which to generate
+    unsigned maxLevel = ~0;
+    args.read( "--max-level", maxLevel );  
+
+    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::string tmsPath;
+
+    //Get the first argument that is not an option
+    for(int pos=1;pos<args.argc();++pos)
+    {
+        if (!args.isOption(pos))
+        {
+            tmsPath  = args[ pos ];
+        }
+    }
+
+    if (tmsPath.empty())
+    {
+        return usage( "Please provide a path to a TMS TileMap" );
+    }
+    
+
+    TMSBackFiller backfiller;
+    backfiller.setMinLevel( minLevel );
+    backfiller.setMaxLevel( maxLevel );
+    backfiller.setBounds( bounds );
+    backfiller.process( tmsPath, options.get() );
+}
diff --git a/src/applications/osgearth_boundarygen/BoundaryUtil b/src/applications/osgearth_boundarygen/BoundaryUtil
index 3a66bee..abc33a1 100644
--- a/src/applications/osgearth_boundarygen/BoundaryUtil
+++ b/src/applications/osgearth_boundarygen/BoundaryUtil
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_boundarygen/BoundaryUtil.cpp b/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
index f49e671..8546a5a 100644
--- a/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
+++ b/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_boundarygen/VertexCollectionVisitor b/src/applications/osgearth_boundarygen/VertexCollectionVisitor
index 7f7fcc3..4cd68b6 100644
--- a/src/applications/osgearth_boundarygen/VertexCollectionVisitor
+++ b/src/applications/osgearth_boundarygen/VertexCollectionVisitor
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp b/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp
index 138c701..5080365 100644
--- a/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp
+++ b/src/applications/osgearth_boundarygen/VertexCollectionVisitor.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_boundarygen/boundarygen.cpp b/src/applications/osgearth_boundarygen/boundarygen.cpp
index 3c4dbd9..749b23a 100644
--- a/src/applications/osgearth_boundarygen/boundarygen.cpp
+++ b/src/applications/osgearth_boundarygen/boundarygen.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_city/osgearth_city.cpp b/src/applications/osgearth_city/osgearth_city.cpp
index 633c4d3..f520d31 100644
--- a/src/applications/osgearth_city/osgearth_city.cpp
+++ b/src/applications/osgearth_city/osgearth_city.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_clamp/osgearth_clamp.cpp b/src/applications/osgearth_clamp/osgearth_clamp.cpp
index 6f5d7fb..ff9c4d8 100644
--- a/src/applications/osgearth_clamp/osgearth_clamp.cpp
+++ b/src/applications/osgearth_clamp/osgearth_clamp.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp b/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp
index 2c8d393..4b3bfa7 100644
--- a/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp
+++ b/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -706,41 +706,44 @@ main(int argc, char** argv)
         {
             ImageLayer* layer = mapNode->getMap()->getImageLayerAt( i );
 
-            if ( useHSL )
+            if ( layer->getEnabled() && layer->getVisible() )
             {
-                HSLColorFilter* filter = new HSLColorFilter();
-                layer->addColorFilter( filter );
-                HSL::addControls( filter, box, i );
-            }
-            else if ( useRGB )
-            {
-                RGBColorFilter* filter = new RGBColorFilter();
-                layer->addColorFilter( filter );
-                RGB::addControls( filter, box, i );
-            }
-            else if ( useCMYK )
-            {
-                CMYKColorFilter* filter = new CMYKColorFilter();
-                layer->addColorFilter( filter );
-                CMYK::addControls( filter, box, i );
-            }
-            else if ( useBC )
-            {
-                BrightnessContrastColorFilter* filter = new BrightnessContrastColorFilter();
-                layer->addColorFilter( filter );
-                BC::addControls( filter, box, i );
-            }
-            else if ( useGamma )
-            {
-                GammaColorFilter* filter = new GammaColorFilter();
-                layer->addColorFilter( filter );
-                GAMMA::addControls( filter, box, i );
-            }
-            else if ( useChromaKey )
-            {
-                ChromaKeyColorFilter* filter = new ChromaKeyColorFilter();
-                layer->addColorFilter( filter );
-                CHROMAKEY::addControls( filter, box , i );
+                if ( useHSL )
+                {
+                    HSLColorFilter* filter = new HSLColorFilter();
+                    layer->addColorFilter( filter );
+                    HSL::addControls( filter, box, i );
+                }
+                else if ( useRGB )
+                {
+                    RGBColorFilter* filter = new RGBColorFilter();
+                    layer->addColorFilter( filter );
+                    RGB::addControls( filter, box, i );
+                }
+                else if ( useCMYK )
+                {
+                    CMYKColorFilter* filter = new CMYKColorFilter();
+                    layer->addColorFilter( filter );
+                    CMYK::addControls( filter, box, i );
+                }
+                else if ( useBC )
+                {
+                    BrightnessContrastColorFilter* filter = new BrightnessContrastColorFilter();
+                    layer->addColorFilter( filter );
+                    BC::addControls( filter, box, i );
+                }
+                else if ( useGamma )
+                {
+                    GammaColorFilter* filter = new GammaColorFilter();
+                    layer->addColorFilter( filter );
+                    GAMMA::addControls( filter, box, i );
+                }
+                else if ( useChromaKey )
+                {
+                    ChromaKeyColorFilter* filter = new ChromaKeyColorFilter();
+                    layer->addColorFilter( filter );
+                    CHROMAKEY::addControls( filter, box , i );
+                }
             }
         }
     }
diff --git a/src/applications/osgearth_contour/osgearth_contour.cpp b/src/applications/osgearth_contour/osgearth_contour.cpp
index 1d1479d..30007f1 100644
--- a/src/applications/osgearth_contour/osgearth_contour.cpp
+++ b/src/applications/osgearth_contour/osgearth_contour.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -49,7 +49,7 @@ const char* vertexShader =
     "uniform   float contour_xferMax; \n"
     "varying   float contour_lookup; \n"
 
-    "void setupContour() \n"
+    "void setupContour(inout vec4 VertexModel) \n"
     "{ \n"
     "    float height = osgearth_elevData[3]; \n"
     "    float height_normalized = (height-contour_xferMin)/contour_xferRange; \n"
@@ -92,9 +92,8 @@ osg::StateSet* createStateSet( osg::TransferFunction1D* xfer, int unit )
     // Install the shaders. We also bind osgEarth's elevation data attribute, which the 
     // terrain engine automatically generates at the specified location.
     VirtualProgram* vp = new VirtualProgram();
-    vp->installDefaultColoringAndLightingShaders();
-    vp->setFunction( "setupContour", vertexShader,   ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
-    vp->setFunction( "colorContour", fragmentShader, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+    vp->setFunction( "setupContour", vertexShader,   ShaderComp::LOCATION_VERTEX_MODEL);
+    vp->setFunction( "colorContour", fragmentShader, ShaderComp::LOCATION_FRAGMENT_COLORING );
     vp->addBindAttribLocation( "osgearth_elevData", osg::Drawable::ATTRIBUTE_6 );
     stateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
 
diff --git a/src/applications/osgearth_controls/osgearth_controls.cpp b/src/applications/osgearth_controls/osgearth_controls.cpp
index 70e1264..a7c4d54 100644
--- a/src/applications/osgearth_controls/osgearth_controls.cpp
+++ b/src/applications/osgearth_controls/osgearth_controls.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_elevation/osgearth_elevation.cpp b/src/applications/osgearth_elevation/osgearth_elevation.cpp
index 3eb1c17..876ae8b 100644
--- a/src/applications/osgearth_elevation/osgearth_elevation.cpp
+++ b/src/applications/osgearth_elevation/osgearth_elevation.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp b/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp
index 9871a6c..a08fca5 100644
--- a/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp
+++ b/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp b/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp
index 55b5ef9..b4398af 100644
--- a/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp
+++ b/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp b/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp
index e09b4f0..86cebaa 100644
--- a/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp
+++ b/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp b/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp
index 116395b..8875071 100644
--- a/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp
+++ b/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -151,7 +151,7 @@ main(int argc, char** argv)
         if ( mapNode )
         {
             // install the Feature Manipulation tool.
-            s_manipTool = new FeatureManipTool( mapNode );
+            s_manipTool = new FeatureManipTool( mapNode, true );
             viewer.addEventHandler( s_manipTool );
 
             s_manipTool->addCallback( new ToggleUIStateCallback() );
diff --git a/src/applications/osgearth_featurequery/osgearth_featurequery.cpp b/src/applications/osgearth_featurequery/osgearth_featurequery.cpp
index 5ff5ad0..4aee9fd 100644
--- a/src/applications/osgearth_featurequery/osgearth_featurequery.cpp
+++ b/src/applications/osgearth_featurequery/osgearth_featurequery.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_features/osgearth_features.cpp b/src/applications/osgearth_features/osgearth_features.cpp
index 89bea6d..44ff332 100644
--- a/src/applications/osgearth_features/osgearth_features.cpp
+++ b/src/applications/osgearth_features/osgearth_features.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_graticule/osgearth_graticule.cpp b/src/applications/osgearth_graticule/osgearth_graticule.cpp
index f719aae..afc3d85 100644
--- a/src/applications/osgearth_graticule/osgearth_graticule.cpp
+++ b/src/applications/osgearth_graticule/osgearth_graticule.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -87,6 +87,9 @@ main(int argc, char** argv)
     else // if ( isGeodetic )
     {
         GeodeticGraticule* gr = new GeodeticGraticule( mapNode );
+        GeodeticGraticuleOptions o = gr->getOptions();
+        o.lineStyle()->getOrCreate<LineSymbol>()->stroke()->color().set(1,0,0,1);
+        gr->setOptions( o );
         root->addChild( gr );
         formatter = new LatLongFormatter();
     }
diff --git a/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp b/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp
index 4616d1d..f2787bf 100644
--- a/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp
+++ b/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_los/osgearth_los.cpp b/src/applications/osgearth_los/osgearth_los.cpp
index f8610a3..53a247e 100644
--- a/src/applications/osgearth_los/osgearth_los.cpp
+++ b/src/applications/osgearth_los/osgearth_los.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_manip/osgearth_manip.cpp b/src/applications/osgearth_manip/osgearth_manip.cpp
index ea9d58e..3b52271 100644
--- a/src/applications/osgearth_manip/osgearth_manip.cpp
+++ b/src/applications/osgearth_manip/osgearth_manip.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_map/osgearth_map.cpp b/src/applications/osgearth_map/osgearth_map.cpp
index 769a3b2..89e9802 100644
--- a/src/applications/osgearth_map/osgearth_map.cpp
+++ b/src/applications/osgearth_map/osgearth_map.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_measure/osgearth_measure.cpp b/src/applications/osgearth_measure/osgearth_measure.cpp
index 83a659d..a649c4d 100644
--- a/src/applications/osgearth_measure/osgearth_measure.cpp
+++ b/src/applications/osgearth_measure/osgearth_measure.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_minimap/CMakeLists.txt b/src/applications/osgearth_minimap/CMakeLists.txt
new file mode 100644
index 0000000..5ca9224
--- /dev/null
+++ b/src/applications/osgearth_minimap/CMakeLists.txt
@@ -0,0 +1,7 @@
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
+SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
+
+SET(TARGET_SRC osgearth_minimap.cpp )
+
+#### end var setup  ###
+SETUP_APPLICATION(osgearth_minimap)
\ No newline at end of file
diff --git a/src/applications/osgearth_minimap/osgearth_minimap.cpp b/src/applications/osgearth_minimap/osgearth_minimap.cpp
new file mode 100644
index 0000000..bed8e6a
--- /dev/null
+++ b/src/applications/osgearth_minimap/osgearth_minimap.cpp
@@ -0,0 +1,142 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osg/Notify>
+#include <osgViewer/Viewer>
+#include <osgEarthUtil/EarthManipulator>
+#include <osgEarthUtil/ExampleResources>
+#include <osgEarthAnnotation/PlaceNode>
+#include <osgViewer/CompositeViewer>
+#include <osgEarthDrivers/gdal/GDALOptions>
+
+#define LC "[viewer] "
+
+using namespace osgEarth;
+using namespace osgEarth::Util;
+using namespace osgEarth::Annotation;
+using namespace osgEarth::Drivers;
+
+
+/**
+ * Makes a simple projected MapNode that contains a basemap of the world
+ */
+MapNode* makeMiniMapNode( ) {    
+    MapOptions mapOpt;
+    mapOpt.coordSysType() = MapOptions::CSTYPE_PROJECTED;    
+    Map* map = new Map( mapOpt );    
+
+    GDALOptions basemapOpt;
+    basemapOpt.url() = "../data/world.tif";
+    map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) );
+
+    // That's it, the map is ready; now create a MapNode to render the Map:
+    MapNodeOptions mapNodeOptions;
+    mapNodeOptions.enableLighting() = false;    
+
+    return new MapNode( map, mapNodeOptions );
+}
+
+int
+main(int argc, char** argv)
+{
+    osg::ArgumentParser arguments(&argc,argv);
+    if ( arguments.read("--stencil") )
+        osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );
+
+    //Setup a CompositeViewer
+    osgViewer::CompositeViewer viewer(arguments);
+
+    //Setup our main view that will show the loaded earth file.
+    osgViewer::View* mainView = new osgViewer::View();
+    mainView->getCamera()->setNearFarRatio(0.00002);
+    mainView->setCameraManipulator( new EarthManipulator() );      
+    mainView->setUpViewInWindow( 50, 50, 800, 800 );
+    viewer.addView( mainView );
+    
+    //Setup a MiniMap View that will be embedded in the main view
+    int miniMapWidth = 400;
+    int miniMapHeight = 200;
+    osgViewer::View* miniMapView = new osgViewer::View();
+    miniMapView->getCamera()->setNearFarRatio(0.00002);
+    miniMapView->getCamera()->setViewport( 0, 0, miniMapWidth, miniMapHeight);    
+    miniMapView->setCameraManipulator( new EarthManipulator() );    
+    miniMapView->getCamera()->setClearColor( osg::Vec4(0,0,0,0));
+    miniMapView->getCamera()->setProjectionResizePolicy( osg::Camera::FIXED );
+    miniMapView->getCamera()->setProjectionMatrixAsPerspective(30.0, double(miniMapWidth) / double(miniMapHeight), 1.0, 1000.0);
+    //Share a graphics context with the main view
+    miniMapView->getCamera()->setGraphicsContext( mainView->getCamera()->getGraphicsContext());        
+    viewer.addView( miniMapView );
+    
+    // load an earth file, and support all or our example command-line options
+    // and earth file <external> tags    
+    osg::Node* node = MapNodeHelper().load( arguments, mainView );
+    if ( node )
+    {
+        //Set the main view's scene data to the loaded earth file
+        mainView->setSceneData( node );
+
+        //Setup a group to hold the contents of the MiniMap
+        osg::Group* miniMapGroup = new osg::Group;
+
+        MapNode* miniMapNode = makeMiniMapNode();        
+        miniMapGroup->addChild( miniMapNode );
+       
+        //Get the main MapNode so we can do tranformations between it and our minimap
+        MapNode* mainMapNode = MapNode::findMapNode( node );
+                               
+        //Set the scene data for the minimap
+        miniMapView->setSceneData( miniMapGroup );        
+
+        //Add a marker we can move around with the main view's eye point
+        Style markerStyle;
+        markerStyle.getOrCreate<IconSymbol>()->url()->setLiteral( "../data/placemark32.png" );
+        PlaceNode* eyeMarker = new PlaceNode(miniMapNode, GeoPoint(miniMapNode->getMapSRS(), 0, 0), "", markerStyle);
+        miniMapGroup->addChild( eyeMarker );        
+
+        while (!viewer.done())
+        {
+            //Reset the viewport so that the camera's viewport is static and doesn't resize with window resizes
+            miniMapView->getCamera()->setViewport( 0, 0, miniMapWidth, miniMapHeight);    
+
+            //Get the eye point of the main view
+            osg::Vec3d eye, up, center;
+            mainView->getCamera()->getViewMatrixAsLookAt( eye, center, up );
+
+            //Turn the eye into a geopoint and transform it to the minimap's SRS
+            GeoPoint eyeGeo;
+            eyeGeo.fromWorld( mainMapNode->getMapSRS(), eye );
+            eyeGeo.transform( miniMapNode->getMapSRS());
+
+            //We want the marker to be positioned at elevation 0, so zero out any elevation in the eye point
+            eyeGeo.z() = 0;           
+        
+            //Set the position of the marker
+            eyeMarker->setPosition( eyeGeo );
+
+            viewer.frame();
+        }        
+    }
+    else
+    {
+        OE_NOTICE 
+            << "\nUsage: " << argv[0] << " file.earth" << std::endl
+            << MapNodeHelper().usage() << std::endl;
+    }
+    return 0;
+}
diff --git a/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp b/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp
index 9b627d1..98d64c0 100644
--- a/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp
+++ b/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_overlayviewer/osgearth_overlayviewer.cpp b/src/applications/osgearth_overlayviewer/osgearth_overlayviewer.cpp
index 053eb53..e815441 100644
--- a/src/applications/osgearth_overlayviewer/osgearth_overlayviewer.cpp
+++ b/src/applications/osgearth_overlayviewer/osgearth_overlayviewer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -36,8 +36,8 @@ using namespace osgEarth::Symbology;
 //------------------------------------------------------------------------
 
 static CheckBoxControl* s_cameraCheck;
-static CheckBoxControl* s_overlayCheck;
-static CheckBoxControl* s_intersectionCheck;
+//static CheckBoxControl* s_overlayCheck;
+//static CheckBoxControl* s_intersectionCheck;
 static CheckBoxControl* s_rttCheck;
 
 namespace
@@ -108,7 +108,7 @@ namespace
 
                     toggle(_parent, "camera", s_cameraCheck->getValue());
                     //toggle(_parent, "overlay", s_overlayCheck->getValue());
-                    toggle(_parent, "intersection", s_intersectionCheck->getValue());
+                    //toggle(_parent, "intersection", s_intersectionCheck->getValue());
                     toggle(_parent, "rtt", s_rttCheck->getValue());
 
                     aa.requestRedraw();
@@ -134,16 +134,19 @@ setupOverlayView( osgViewer::View* view, osg::Group* parent, MapNode* mapNode )
             camBox->addControl(s_cameraCheck = new CheckBoxControl(true, new Toggle(parent,"camera")));
             camBox->addControl(new LabelControl("Camera", Color("#00ff00")));
         }
+
         //HBox* overlayBox = v->addControl(new HBox());
         //{
         //    overlayBox->addControl(s_overlayCheck = new CheckBoxControl(false, new Toggle(parent,"overlay")));
         //    overlayBox->addControl(new LabelControl("Overlay", Color("#00ffff")));
         //}
-        HBox* isectBox = v->addControl(new HBox());
-        {
-            isectBox->addControl(s_intersectionCheck = new CheckBoxControl(true, new Toggle(parent,"intersection")));
-            isectBox->addControl(new LabelControl("Intersection",Color("#ff7f00")));
-        }
+
+        //HBox* isectBox = v->addControl(new HBox());
+        //{
+        //    isectBox->addControl(s_intersectionCheck = new CheckBoxControl(true, new Toggle(parent,"intersection")));
+        //    isectBox->addControl(new LabelControl("Intersection",Color("#ff7f00")));
+        //}
+
         HBox* rttBox = v->addControl(new HBox());
         {
             rttBox->addControl(s_rttCheck = new CheckBoxControl(true, new Toggle(parent,"rtt")));
diff --git a/src/applications/osgearth_package/osgearth_package.cpp b/src/applications/osgearth_package/osgearth_package.cpp
index 9ee48bd..46e074e 100644
--- a/src/applications/osgearth_package/osgearth_package.cpp
+++ b/src/applications/osgearth_package/osgearth_package.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_package_qt/CMakeLists.txt b/src/applications/osgearth_package_qt/CMakeLists.txt
new file mode 100644
index 0000000..7f7a9a6
--- /dev/null
+++ b/src/applications/osgearth_package_qt/CMakeLists.txt
@@ -0,0 +1,56 @@
+INCLUDE( ${QT_USE_FILE} )
+
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} ${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
+
+SET(UI_FILES
+    ExportDialog.ui
+)
+
+SET(MOC_HDRS
+    PackageQtMainWindow
+    ExportDialog
+    WaitDialog
+)
+
+set(LIB_QT_RCS
+    images.qrc
+)
+
+QT4_ADD_RESOURCES( LIB_RC_SRCS ${LIB_QT_RCS} )
+
+QT4_WRAP_UI( UI_HDRS ${UI_FILES} )
+
+QT4_WRAP_CPP( UI_SRCS ${UI_HDRS} )
+
+QT4_WRAP_CPP( MOC_SRCS ${MOC_HDRS} OPTIONS "-f" )
+
+SET(TARGET_H
+    PackageQtMainWindow
+    ExportDialog
+    WaitDialog
+    SceneController.h
+    TMSExporter.h
+    ${UI_HDRS}
+    ${LIB_QT_RCS}
+)
+
+SET(TARGET_SRC
+    ${UI_SRCS}
+    ${MOC_SRCS}
+    ${LIB_RC_SRCS}
+    ExportDialog.cpp
+    SceneController.cpp
+    TMSExporter.cpp
+    WaitDialog.cpp
+    package_qt.cpp
+)
+
+SET(TARGET_ADDED_LIBRARIES
+    osgEarthQt
+    ${QT_QTCORE_LIBRARY}
+    ${QT_QTGUI_LIBRARY}
+    ${QT_QTOPENGL_LIBRARY}
+)
+
+#### end var setup  ###
+SETUP_APPLICATION(osgearth_package_qt)
diff --git a/src/applications/osgearth_package_qt/ExportDialog b/src/applications/osgearth_package_qt/ExportDialog
new file mode 100644
index 0000000..a2ee942
--- /dev/null
+++ b/src/applications/osgearth_package_qt/ExportDialog
@@ -0,0 +1,67 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 TILER_TOOL_EXPORTDIALOG
+#define TILER_TOOL_EXPORTDIALOG 1
+
+#include "ui_ExportDialog.h"
+
+namespace PackageQt
+{
+  class ExportDialog : public QDialog
+  {
+	  Q_OBJECT
+
+	  public:
+      ExportDialog(const std::string& dir="", const std::string& boundsString="");
+
+      std::string getExportPath() const { return _ui.exportPathEdit->text().toUtf8().data(); }
+
+      bool exportEarthFile() const { return _ui.earthFileCheckBox->isChecked(); }
+      std::string getEarthFilePath() const { return _ui.earthFilePathEdit->text().toUtf8().data(); }
+
+      bool maxLevelEnabled() const { return _ui.maxLevelCheckBox->isChecked(); }
+      int getMaxLevel() const { return _ui.maxLevelCheckBox->isChecked() ? _ui.maxLevelSpinBox->value() : ~0; }
+
+      //bool overrideExtension() const { return _ui.extensionCheckBox->isChecked(); }
+      //std::string getExtension() const { return _ui.extensionComboBox->currentText().toUtf8().data(); }
+
+      bool overwriteExisting() const { return _ui.overwriteCheckBox->isChecked(); }
+
+      bool keepEmpties() const { return _ui.keepEmptiesCheckBox->isChecked(); }
+
+      bool useBounds() const { return _ui.boundsCheckBox->isChecked(); }
+
+	  private slots:
+		  void showExportBrowse();
+      void updateEarthFilePathEdit();
+      void updateMaxLevelSpinBox();
+      //void updateExtensionComboBox();
+      void validateAndAccept();
+
+	  private:		
+		  Ui::ExportDialog _ui;
+      
+		  void initUi(const std::string& dir, const std::string& boundsString);
+  };
+}
+
+
+
+#endif //TILER_TOOL_EXPORTDIALOG
\ No newline at end of file
diff --git a/src/applications/osgearth_package_qt/ExportDialog.cpp b/src/applications/osgearth_package_qt/ExportDialog.cpp
new file mode 100644
index 0000000..ddc0b01
--- /dev/null
+++ b/src/applications/osgearth_package_qt/ExportDialog.cpp
@@ -0,0 +1,108 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 "ExportDialog"
+
+#include <QFileDialog>
+
+using namespace PackageQt;
+
+
+ExportDialog::ExportDialog(const std::string& dir, const std::string& boundsString)
+{
+  initUi(dir, boundsString);
+}
+
+void ExportDialog::initUi(const std::string& dir, const std::string& boundsString)
+{
+	_ui.setupUi(this);
+
+  _ui.errorLabel->setStyleSheet("color: red");
+
+  _ui.exportPathEdit->setText(tr(dir.c_str()));
+
+  if (boundsString.length() > 0)
+  {
+    _ui.boundsLabel->setText(tr(boundsString.c_str()));
+    _ui.boundsLabel->setEnabled(true);
+    _ui.boundsCheckBox->setEnabled(true);
+    _ui.boundsCheckBox->setChecked(true);
+  }
+
+  QObject::connect(_ui.exportPathBrowseButton, SIGNAL(clicked()), this, SLOT(showExportBrowse()));
+  QObject::connect(_ui.earthFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateEarthFilePathEdit()));
+	QObject::connect(_ui.maxLevelCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateMaxLevelSpinBox()));
+//  QObject::connect(_ui.extensionCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateExtensionComboBox()));
+  QObject::connect(_ui.okButton, SIGNAL(clicked()), this, SLOT(validateAndAccept()));
+}
+
+void ExportDialog::showExportBrowse()
+{
+  QString dir = QFileDialog::getExistingDirectory(this, tr("Export Directory"),
+    _ui.exportPathEdit->text().length() > 0 ? _ui.exportPathEdit->text() : QDir::homePath(),
+    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
+
+	if (!dir.isNull())
+		_ui.exportPathEdit->setText(dir);
+}
+
+//void ExportDialog::showEarthFileBrowse()
+//{
+//  QString path = QFileDialog::getSaveFileName(this, tr("Earth File"),
+//    _ui.earthFilePathEdit->text().length() > 0 ? _ui.earthFilePathEdit->text() : QDir::homePath() + QDir::separator() + "out.earth",
+//    tr("Earth Files (*.earth)"));
+//
+//	if (!path.isNull())
+//		_ui.earthFilePathEdit->setText(path);
+//}
+
+void ExportDialog::updateEarthFilePathEdit()
+{
+  _ui.earthFilePathEdit->setEnabled(_ui.earthFileCheckBox->isChecked());
+}
+
+void ExportDialog::updateMaxLevelSpinBox()
+{
+  _ui.maxLevelSpinBox->setEnabled(_ui.maxLevelCheckBox->isChecked());
+}
+
+//void ExportDialog::updateExtensionComboBox()
+//{
+//  _ui.extensionComboBox->setEnabled(_ui.extensionCheckBox->isChecked());
+//}
+
+void ExportDialog::validateAndAccept()
+{
+  std::string errMsg = "ERROR: ";
+  if (_ui.exportPathEdit->text().isEmpty())
+  {
+    errMsg += "Export path not set";
+  }
+  else if (_ui.earthFileCheckBox->isChecked() && _ui.earthFilePathEdit->text().isEmpty())
+  {
+    errMsg += "Earth file name not set";
+  }
+  else
+  {
+    accept();
+    return;
+  }
+
+  _ui.errorLabel->setText(QString(errMsg.c_str()));
+}
\ No newline at end of file
diff --git a/src/applications/osgearth_package_qt/ExportDialog.ui b/src/applications/osgearth_package_qt/ExportDialog.ui
new file mode 100644
index 0000000..bed70fc
--- /dev/null
+++ b/src/applications/osgearth_package_qt/ExportDialog.ui
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ExportDialog</class>
+ <widget class="QDialog" name="ExportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>565</width>
+    <height>298</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Export Settings</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Export location:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="exportPathEdit"/>
+     </item>
+     <item>
+      <widget class="QPushButton" name="exportPathBrowseButton">
+       <property name="text">
+        <string>Browse</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QCheckBox" name="earthFileCheckBox">
+       <property name="text">
+        <string>Generate .earth file:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="earthFilePathEdit">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>out.earth</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Fixed</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>5</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_7">
+     <item>
+      <widget class="QCheckBox" name="boundsCheckBox">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Bounds:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="boundsLabel">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>unspecified</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Details</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QCheckBox" name="maxLevelCheckBox">
+          <property name="text">
+           <string>Max level (no max level = infinity):</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QSpinBox" name="maxLevelSpinBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="minimum">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <number>20</number>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_5">
+        <item>
+         <widget class="QCheckBox" name="overwriteCheckBox">
+          <property name="text">
+           <string>Overwrite existing tiles</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_6">
+        <item>
+         <widget class="QCheckBox" name="keepEmptiesCheckBox">
+          <property name="text">
+           <string>Keep empty (transparent) image tiles</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_4">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QLabel" name="errorLabel">
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>131</width>
+         <height>31</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okButton">
+       <property name="text">
+        <string>OK</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>cancelButton</sender>
+   <signal>clicked()</signal>
+   <receiver>ExportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>554</x>
+     <y>283</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>179</x>
+     <y>282</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/applications/osgearth_package_qt/PackageQtMainWindow b/src/applications/osgearth_package_qt/PackageQtMainWindow
new file mode 100644
index 0000000..545f601
--- /dev/null
+++ b/src/applications/osgearth_package_qt/PackageQtMainWindow
@@ -0,0 +1,428 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osgEarthQt/Common>
+#include <osgEarthQt/MultiViewerWidget>
+#include <osgEarthQt/DataManager>
+#include <osgEarthQt/LayerManagerWidget>
+#include <osgEarthQt/MapCatalogWidget>
+#include <osgEarthQt/TerrainProfileWidget>
+#include <osgEarthQt/ViewerWidget>
+
+#include <osgEarth/Common>
+#include <osgEarthDrivers/gdal/GDALOptions>
+#include <osgEarth/GeoData>
+#include <osgEarth/Map>
+#include <osgEarth/Progress>
+
+#include <osgDB/FileNameUtils>
+
+#include <QAction>
+#include <QDockWidget>
+#include <QtGui>
+#include <QMainWindow>
+#include <QMetaObject>
+#include <QProgressDialog>
+#include <QToolBar>
+
+#include "SceneController.h"
+#include "TMSExporter.h"
+#include "ExportDialog"
+#include "WaitDialog"
+
+namespace
+{
+  class ExportProgressCallback : public osgEarth::ProgressCallback
+  {
+  public:
+    ExportProgressCallback(QProgressDialog* dialog)
+      : _dialog(dialog)
+    {
+    }
+
+    virtual ~ExportProgressCallback() { }
+
+    bool reportProgress(double current, double total, unsigned currentStage, unsigned totalStages, const std::string& msg)
+    {
+      if (_dialog)
+      {
+        QMetaObject::invokeMethod(_dialog, "setValue", Qt::QueuedConnection, Q_ARG(int, (current / total) * 100));
+
+        if (_dialog->wasCanceled())
+          return true;
+      }
+
+      return false;
+    }
+
+    void onCompleted()
+    {
+      QMetaObject::invokeMethod(_dialog, "close", Qt::QueuedConnection);
+    }
+
+  private:
+    QProgressDialog* _dialog;
+  };
+
+  struct SceneBoundsSetCallback : public PackageQt::BoundsSetCallback
+  {
+    SceneBoundsSetCallback(QAction* toggleAction) : _action(toggleAction) { }
+
+    void boundsSet(const osg::Vec2d& ll, const osg::Vec2d& ur)
+    {
+      QMetaObject::invokeMethod(_action, "setChecked", Qt::QueuedConnection, Q_ARG(bool, false));
+    }
+
+    QAction* _action;
+  };
+}
+
+namespace PackageQt
+{
+
+class PackageQtMainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+
+  PackageQtMainWindow(osgEarth::QtGui::ViewerWidget* viewerWidget, PackageQt::SceneController* controller, PackageQt::TMSExporter* exporter)
+        : _viewerWidget(viewerWidget), _controller(controller), _exporter(exporter), _compositeViewerWidget(0L), _lastDir("")
+    {
+        _manager = new osgEarth::QtGui::DataManager(_controller->mapNode());
+
+        initUi();
+
+        if (_viewerWidget)
+        {
+          setCentralWidget(_viewerWidget);
+
+          _views.clear();
+          _viewerWidget->getViews( _views );
+        }
+
+        reloadDisplay();
+    }
+
+
+private slots:
+
+    void openEarthFile()
+    {
+      QString filePath = QFileDialog::getOpenFileName(this, tr("Open an earth file"), _lastDir, tr("Earth files (*.earth)"));
+      if (!filePath.isNull())
+      {
+        _lastDir = QFileInfo(filePath).path();
+
+        PackageQt::WaitDialog msg(tr("Loading .earth file. Please wait."));
+        msg.show();
+        msg.repaint();
+
+        _controller->loadEarthFile(filePath.toStdString());
+        _manager = new osgEarth::QtGui::DataManager(_controller->mapNode());
+        reloadDisplay();
+
+        msg.hide();
+      }
+    }
+
+    void addImageLayer()
+    {
+      bool pathSet = false;
+
+      QStringList filePaths = QFileDialog::getOpenFileNames(this, tr("Add an image layer"), _lastDir, tr("Images (*.tif *.ecw);;All files (*.*)"));
+
+      _manager->map()->beginUpdate();
+
+      for (int i = 0; i < filePaths.size(); i++)
+      {
+        QString filePath = filePaths.at(i);
+        if (!filePath.isNull())
+        {
+          if (!pathSet)
+          {
+            _lastDir = QFileInfo(filePath).path();
+            pathSet = true;
+          }
+
+          osgEarth::Drivers::GDALOptions layerOpt;
+          layerOpt.url() = osgEarth::URI(filePath.toStdString());
+          
+          std::string fileName = osgDB::getSimpleFileName(filePath.toStdString());
+          osg::ref_ptr<osgEarth::ImageLayer> newLayer = new osgEarth::ImageLayer(osgEarth::ImageLayerOptions(fileName, layerOpt));
+
+          _manager->map()->addImageLayer(newLayer.get());
+        }
+      }
+
+      _manager->map()->endUpdate();
+    }
+
+    //void addImageFolder()
+    //{
+    //}
+
+    void addElevationLayer()
+    {
+      bool pathSet = false;
+
+      QStringList filePaths = QFileDialog::getOpenFileNames(this, tr("Add an elevation layer"), _lastDir, tr("Elevation files (*.tif *.dt*);;All files (*.*)"));
+      
+      _manager->map()->beginUpdate();
+
+      for (int i=0; i < filePaths.size(); i++)
+      {
+        QString filePath = filePaths.at(i);
+        if (!filePath.isNull())
+        {
+          if (!pathSet)
+          {
+            _lastDir = QFileInfo(filePath).path();
+            pathSet = true;
+          }
+
+          osgEarth::Drivers::GDALOptions layerOpt;
+          layerOpt.url() = osgEarth::URI(filePath.toStdString());
+          
+          std::string fileName = osgDB::getSimpleFileName(filePath.toStdString());
+          osg::ref_ptr<osgEarth::ElevationLayer> newLayer = new osgEarth::ElevationLayer(osgEarth::ElevationLayerOptions(fileName, layerOpt));
+
+          _manager->map()->addElevationLayer(newLayer.get());
+        }
+      }
+
+      _manager->map()->endUpdate();
+    }
+
+    //void addElevationFolder()
+    //{
+    //}
+
+    void exportRepo()
+    {
+        if (_exporter)
+        {
+          PackageQt::ExportDialog exportDialog(_lastDir.toStdString(), _controller->getBoundsString());
+	        if (exportDialog.exec() == QDialog::Accepted)
+	        {
+            //QProgressDialog* progress = new QProgressDialog(tr("Exporting. Please wait."), tr("Cancel"), 0, 100, this, Qt::CustomizeWindowHint|Qt::WindowTitleHint|Qt::WindowStaysOnTopHint);
+            QProgressDialog* progress = new QProgressDialog(tr("Processing layers..."), QString(), 0, 100, this, Qt::CustomizeWindowHint|Qt::WindowTitleHint|Qt::WindowStaysOnTopHint);
+            progress->setAttribute(Qt::WA_DeleteOnClose, true);
+            progress->setWindowTitle(tr("Exporting"));
+            progress->setValue(0);
+            _exporter->setProgressCallback(new ExportProgressCallback(progress));
+
+            progress->setWindowModality(Qt::WindowModal);
+            progress->show();
+
+            _exporter->setKeepEmpties(exportDialog.keepEmpties());
+            _exporter->setMaxLevel(exportDialog.getMaxLevel());
+
+            osg::Vec2d ll = _controller->getBoundsLL();
+            osg::Vec2d ur = _controller->getBoundsUR();
+
+            std::vector<osgEarth::Bounds> bounds;
+            if (exportDialog.useBounds() && (ur - ll).length() > 0.0)
+              bounds.push_back(osgEarth::Bounds(ll.x(), ll.y(), ur.x(), ur.y()));
+
+            _exportThread = new TMSExporterWorkerThread(
+              _exporter, 
+              _controller->mapNode(),
+              exportDialog.getExportPath(),
+              bounds,
+              (exportDialog.exportEarthFile() ? exportDialog.getEarthFilePath() : ""),
+              exportDialog.overwriteExisting(),
+              "" /*(exportDialog.overrideExtension() ? exportDialog.getExtension() : "")*/);
+
+            _exportThread->start();
+          }
+        }
+    }
+
+    void getBoundingBox(bool checked)
+    {
+      if (!_controller)
+        return;
+
+      if (checked)
+        _controller->captureBounds(new SceneBoundsSetCallback(_bboxAction));
+      else
+        _controller->endBoundsCapture();
+
+    }
+
+    void clearBoundingBox()
+    {
+      if (_controller)
+        _controller->clearBounds();
+    }
+
+protected:
+
+    void closeEvent(QCloseEvent *event)
+    {
+        if (_viewerWidget)
+        {
+            //_viewerWidget->getViewer()->setSceneData(0);
+            //_viewerWidget->getViewer()->frame();
+            _viewerWidget->getViewer()->setDone(true);
+        }
+
+        if (_compositeViewerWidget)
+        {
+            _compositeViewerWidget/*->getViewer()*/->setDone(true);
+        }
+
+        event->accept();
+    }
+
+private:
+
+    void initUi()
+    {
+        setWindowTitle(tr("osgEarth Package Qt"));
+        setWindowIcon(QIcon(":/images/export.png"));
+
+        createActions();
+        createToolbars();
+        createDockWidgets();
+    }
+
+    void reloadDisplay()
+    {
+      if (_controller)
+      {
+        // create a second catalog widget for viewpoints
+        osgEarth::QtGui::MapCatalogWidget* vpCatalog = new osgEarth::QtGui::MapCatalogWidget(_manager, osgEarth::QtGui::MapCatalogWidget::VIEWPOINTS);
+        vpCatalog->setActiveViews(_views);
+        _vpDock->setWidget(vpCatalog);
+
+        // create layer manager widget and add as a docked widget on the right
+        osgEarth::QtGui::LayerManagerWidget* elevLayerManager = new osgEarth::QtGui::LayerManagerWidget(_manager, osgEarth::QtGui::LayerManagerWidget::ELEVATION_LAYERS);
+        elevLayerManager->setActiveViews(_views);
+        _elevLayersDock->setWidget(elevLayerManager);
+
+        // create layer manager widget and add as a docked widget on the right
+        osgEarth::QtGui::LayerManagerWidget* imgLayerManager = new osgEarth::QtGui::LayerManagerWidget(_manager, osgEarth::QtGui::LayerManagerWidget::IMAGE_LAYERS);
+        imgLayerManager->setActiveViews(_views);
+        _imgLayersDock->setWidget(imgLayerManager);
+      }
+    }
+
+	  void createActions()
+    {
+        _openEarthFileAction = new QAction(QIcon(":/images/earth.png"), tr("&Open .earth file"), this);
+        _openEarthFileAction->setToolTip(tr("Open .earth file"));
+        connect(_openEarthFileAction, SIGNAL(triggered()), this, SLOT(openEarthFile()));
+        _openEarthFileAction->setDisabled(!_manager.valid());
+
+        _addImageLayerAction = new QAction(QIcon(":/images/add_image.png"), tr("&Add Imagery"), this);
+        _addImageLayerAction->setToolTip(tr("Add imagery"));
+        connect(_addImageLayerAction, SIGNAL(triggered()), this, SLOT(addImageLayer()));
+        _addImageLayerAction->setDisabled(!_manager.valid());
+
+        //_addImageFolderAction = new QAction(QIcon(":/images/add_image_folder.png"), tr("&Add Folder of Imagery"), this);
+        //_addImageFolderAction->setToolTip(tr("Add folder of imagery"));
+        //connect(_addImageFolderAction, SIGNAL(triggered()), this, SLOT(addImageFolder()));
+        //_addImageFolderAction->setDisabled(!_manager.valid());
+
+        _addElevationLayerAction = new QAction(QIcon(":/images/add_elevation.png"), tr("&Add Elevation"), this);
+        _addElevationLayerAction->setToolTip(tr("Add elevation"));
+        connect(_addElevationLayerAction, SIGNAL(triggered()), this, SLOT(addElevationLayer()));
+        _addElevationLayerAction->setDisabled(!_manager.valid());
+
+        //_addElevationFolderAction = new QAction(QIcon(":/images/add_elevation_folder.png"), tr("&Add Folder of Elevation"), this);
+        //_addElevationFolderAction->setToolTip(tr("Add folder of elevation"));
+        //connect(_addElevationFolderAction, SIGNAL(triggered()), this, SLOT(addElevationFolder()));
+        //_addElevationFolderAction->setDisabled(!_manager.valid());
+
+        _exportAction = new QAction(QIcon(":/images/export.png"), tr(""), this);
+        _exportAction->setToolTip(tr("Export"));
+        connect(_exportAction, SIGNAL(triggered()), this, SLOT(exportRepo()));
+
+        _bboxAction = new QAction(QIcon(":/images/bbox.png"), tr(""), this);
+        _bboxAction->setToolTip(tr("Specify bounding box"));
+        _bboxAction->setCheckable(true);
+        connect(_bboxAction, SIGNAL(triggered(bool)), this, SLOT(getBoundingBox(bool)));
+
+        _bboxClearAction = new QAction(QIcon(":/images/bbox_clear.png"), tr(""), this);
+        _bboxClearAction->setToolTip(tr("Clear bounding box"));
+        connect(_bboxClearAction, SIGNAL(triggered()), this, SLOT(clearBoundingBox()));
+    }
+
+	  void createToolbars()
+    {
+        _fileToolbar = addToolBar(tr("File Toolbar"));
+        _fileToolbar->setObjectName(tr("FILE_TOOLBAR"));
+        _fileToolbar->setIconSize(QSize(24, 24));
+        _fileToolbar->addAction(_openEarthFileAction);
+        _fileToolbar->addSeparator();
+        _fileToolbar->addAction(_addImageLayerAction);
+        //_fileToolbar->addAction(_addImageFolderAction);
+        _fileToolbar->addAction(_addElevationLayerAction);
+        //_fileToolbar->addAction(_addElevationFolderAction);
+        _fileToolbar->addSeparator();
+        _fileToolbar->addAction(_bboxAction);
+        _fileToolbar->addAction(_bboxClearAction);
+        _fileToolbar->addSeparator();
+        _fileToolbar->addAction(_exportAction);
+    }
+
+    void createDockWidgets()
+    {
+      _elevLayersDock = new QDockWidget(QWidget::tr("Elevation Layers"));
+      _elevLayersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+      addDockWidget(Qt::LeftDockWidgetArea, _elevLayersDock);
+
+      _imgLayersDock = new QDockWidget(QWidget::tr("Image Layers"));
+      _imgLayersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+      addDockWidget(Qt::LeftDockWidgetArea, _imgLayersDock);
+
+      _vpDock = new QDockWidget;
+      _vpDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+      addDockWidget(Qt::LeftDockWidgetArea, _vpDock);
+    }
+	
+    PackageQt::SceneController* _controller;
+    PackageQt::TMSExporter* _exporter;
+    osg::ref_ptr<TMSExporterWorkerThread> _exportThread;
+
+    osg::ref_ptr<osgEarth::QtGui::DataManager> _manager;
+    osgEarth::QtGui::ViewerWidget* _viewerWidget;
+    osgEarth::QtGui::MultiViewerWidget* _compositeViewerWidget;
+    osgEarth::QtGui::ViewVector _views;
+
+    QAction *_openEarthFileAction;
+    QAction *_addImageLayerAction;
+    //QAction *_addImageFolderAction;
+    QAction *_addElevationLayerAction;
+    //QAction *_addElevationFolderAction;
+    QAction *_exportAction;
+    QAction *_bboxAction;
+    QAction *_bboxClearAction;
+    QToolBar *_fileToolbar;
+    
+    QDockWidget *_vpDock;
+    QDockWidget *_elevLayersDock;
+    QDockWidget *_imgLayersDock;
+
+    QString _lastDir;
+};
+
+}
\ No newline at end of file
diff --git a/src/applications/osgearth_package_qt/SceneController.cpp b/src/applications/osgearth_package_qt/SceneController.cpp
new file mode 100644
index 0000000..3a43543
--- /dev/null
+++ b/src/applications/osgearth_package_qt/SceneController.cpp
@@ -0,0 +1,295 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 "SceneController.h"
+
+#include <osgEarth/Common>
+#include <osgEarth/Map>
+#include <osgEarth/MapNode>
+//#include <osgEarth/StringUtils>
+#include <osgEarthAnnotation/FeatureNode>
+#include <osgEarthUtil/Controls>
+#include <osgEarthUtil/ExampleResources>
+//#include <osgEarthUtil/SkyNode>
+
+
+
+using namespace PackageQt;
+
+#define LC "[SceneController] "
+
+namespace
+{
+  struct BoundingBoxMouseHandler : public osgGA::GUIEventHandler
+  {
+    BoundingBoxMouseHandler(SceneController* controller, bool startCapture=false)
+      : _controller(controller), _mouseDown(false), _capturing(startCapture)
+    {
+    }
+
+    void startCapture()
+    {
+      _capturing = true;
+    }
+
+    void stopCapture()
+    {
+      _capturing = false;
+    }
+
+    bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
+    {
+      if (!_capturing || !_controller)
+        return false;
+
+      osgViewer::View* view = static_cast<osgViewer::View*>(aa.asView());
+
+      if ( ea.getEventType() == osgGA::GUIEventAdapter::PUSH  && ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
+      {
+        osg::Vec3d world;
+        if (_controller->mapNode()->getTerrain()->getWorldCoordsUnderMouse(aa.asView(), ea.getX(), ea.getY(), world))
+        {
+          osgEarth::GeoPoint mapPoint;
+          mapPoint.fromWorld( _controller->mapNode()->getMapSRS(), world );
+          _startPoint = mapPoint;
+        }
+
+        std::cout << "BBox DOWN" << std::endl;
+
+        _mouseDown = true;
+
+        ea.setHandled(true);
+        return true;
+      }
+      else if (ea.getEventType() == osgGA::GUIEventAdapter::DRAG && _mouseDown)
+      {
+        osg::Vec3d world;
+        if (_controller->mapNode()->getTerrain()->getWorldCoordsUnderMouse(aa.asView(), ea.getX(), ea.getY(), world))
+        {
+          osgEarth::GeoPoint mapPoint;
+          mapPoint.fromWorld( _controller->mapNode()->getMapSRS(), world );
+          _controller->setBounds(_startPoint, mapPoint);
+        }
+
+        std::cout << "BBox MOVE" << std::endl;
+
+        ea.setHandled(true);
+        return true;
+      }
+      else if (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE && ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON && _mouseDown)
+      {
+        _mouseDown = false;
+        _capturing = false;
+        _controller->endBoundsCapture();
+
+        std::cout << "BBox UP" << std::endl;
+
+        ea.setHandled(true);
+        return true;
+      }
+
+      return false;
+    }
+
+
+    SceneController* _controller;
+    bool _capturing;
+    bool _mouseDown;
+    osgEarth::GeoPoint _startPoint;
+  };
+}
+
+SceneController::SceneController(osg::Group* root, osgViewer::View* view, const std::string& url)
+: _root(root), _view(view)
+{
+  if (_root.valid() && _view.valid())
+  {
+    //install a canvas for any UI controls we plan to create
+    _canvas = osgEarth::Util::Controls::ControlCanvas::get(_view, false);
+
+    _controlContainer = _canvas->addControl( new osgEarth::Util::Controls::VBox() );
+    _controlContainer->setBackColor( osgEarth::Util::Controls::Color(osgEarth::Util::Controls::Color::Black, 0.8) );
+    _controlContainer->setHorizAlign( osgEarth::Util::Controls::Control::ALIGN_LEFT );
+    _controlContainer->setVertAlign( osgEarth::Util::Controls::Control::ALIGN_BOTTOM );
+
+    root->addChild(_canvas);
+
+    //create the root group for annotations
+    _annoRoot = new osg::Group();
+    root->addChild(_annoRoot);
+  }
+
+  //Setup bounding box style
+  osgEarth::Symbology::LineSymbol* ls = _boundsStyle.getOrCreate<osgEarth::Symbology::LineSymbol>();
+  ls->stroke()->color() = osgEarth::Symbology::Color::Red;
+  ls->stroke()->width() = 3.0f;
+  ls->stroke()->stipple() = 0x0F0F;
+  //ls->tessellation() = 20;
+  _boundsStyle.getOrCreate<osgEarth::Symbology::AltitudeSymbol>()->clamping() = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN;
+  _boundsStyle.getOrCreate<osgEarth::Symbology::AltitudeSymbol>()->technique() = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU;
+
+
+  loadEarthFile(url);
+}
+
+osg::Node* SceneController::loadEarthFile(const std::string& url)
+{
+  if (!_root.valid() || !_view.valid())
+    return 0L;
+
+  if (_earthNode.valid())
+  {
+    _root->removeChild(_earthNode);
+    _earthNode = 0L;
+  }
+
+  //if (_sky.valid())
+  //{
+  //  _root->removeChild(_sky);
+  //  _sky = 0L;
+  //}
+
+  if (_controlContainer.valid())
+    _controlContainer->clearControls();
+
+  if (url.length() > 0)
+    _earthNode = osgDB::readNodeFile( url );
+
+  //Load a blank globe if needed
+  if (!_earthNode.valid())
+    _earthNode = new osgEarth::MapNode(new osgEarth::Map());
+
+  if (_earthNode.valid())
+  {
+    _mapNode = osgEarth::MapNode::findMapNode( _earthNode );
+    if (_mapNode.valid())
+    {
+      _map = _mapNode->getMap();
+
+      //const osgEarth::Config& externals = _mapNode->externalConfig();
+
+      //if (_map->isGeocentric())
+      //{
+      //  // Sky model.
+      //  osgEarth::Config skyConf = externals.child("sky");
+
+      //  double hours = skyConf.value("hours", 12.0);
+
+      //  _sky = new osgEarth::Util::SkyNode(_map);
+      //  _sky->setAutoAmbience( true );
+      //  _sky->setDateTime(2011, 3, 6, hours);
+      //  _sky->attach(_view);
+
+      //  _root->addChild(_sky);
+
+      //  if (_controlContainer.valid())
+      //  {
+      //    osgEarth::Util::Controls::Control* c = osgEarth::Util::SkyControlFactory().create(_sky, _view);
+      //    if ( c )
+      //        _controlContainer->addControl( c );
+      //  }
+      //}
+    }
+
+    _root->addChild(_earthNode);
+
+    //Create new EarthManipulator
+    _view->setCameraManipulator(new osgEarth::Util::EarthManipulator());
+  }
+
+  return _earthNode.get();
+}
+
+void SceneController::captureBounds(BoundsSetCallback* callback)
+{
+  _boundsCallback = callback;
+
+  if (!_guiHandler.valid())
+  {
+    _guiHandler = new BoundingBoxMouseHandler(this, true);
+    _view->addEventHandler(_guiHandler.get());
+  }
+  else
+  {
+    dynamic_cast<BoundingBoxMouseHandler *>(_guiHandler.get())->startCapture();
+  }
+}
+
+void SceneController::endBoundsCapture()
+{
+  if (_guiHandler.valid())
+    dynamic_cast<BoundingBoxMouseHandler *>(_guiHandler.get())->stopCapture();
+
+  if (_boundsCallback.valid())
+    _boundsCallback->boundsSet(_boundsLL, _boundsUR);
+}
+
+void SceneController::clearBounds()
+{
+  if (_bboxNode.valid())
+  {
+    _annoRoot->removeChild(_bboxNode.get());
+    _bboxNode = 0L;
+  }
+
+  _boundsLL.set(0., 0.);
+  _boundsUR.set(0., 0.);
+}
+
+void SceneController::setBounds(const osgEarth::GeoPoint& p1, const osgEarth::GeoPoint& p2)
+{
+  _boundsLL.set(osg::minimum(p1.x(), p2.x()), osg::minimum(p1.y(), p2.y()));
+  _boundsUR.set(osg::maximum(p1.x(), p2.x()), osg::maximum(p1.y(), p2.y()));
+
+  if (_annoRoot.valid())
+  {
+    //TODO: use correct coords here
+    osgEarth::Symbology::Geometry* geom = new osgEarth::Symbology::Polygon();
+    geom->push_back(_boundsLL.x(), _boundsLL.y());
+    geom->push_back(_boundsUR.x(), _boundsLL.y());
+    geom->push_back(_boundsUR.x(), _boundsUR.y());
+    geom->push_back(_boundsLL.x(), _boundsUR.y());
+
+    osgEarth::Features::Feature* feature = new osgEarth::Features::Feature(geom, _mapNode->getMapSRS()->getGeographicSRS(), _boundsStyle);
+
+    if (!_bboxNode.valid())
+    {
+      _bboxNode = new osgEarth::Annotation::FeatureNode(_mapNode, feature);
+      _bboxNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+      _annoRoot->addChild( _bboxNode.get() );
+    }
+    else
+    {
+      _bboxNode->setFeature(feature);
+    }
+  }
+}
+
+std::string SceneController::getBoundsString()
+{
+  std::string str = "";
+  if ((_boundsUR - _boundsLL).length() > 0.0)
+  {
+    std::stringstream ss;
+    ss << "LL( " << _boundsLL.y() << ", " << _boundsLL.x() << " ) UR( " << _boundsUR.y() << ", " << _boundsUR.x() << " )";
+    str = ss.str();
+  }
+
+  return str;
+}
\ No newline at end of file
diff --git a/src/applications/osgearth_package_qt/SceneController.h b/src/applications/osgearth_package_qt/SceneController.h
new file mode 100644
index 0000000..a3cfa78
--- /dev/null
+++ b/src/applications/osgearth_package_qt/SceneController.h
@@ -0,0 +1,82 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 TILER_TOOL_SCENECONTROLLER_H
+#define TILER_TOOL_SCENECONTROLLER_H 1
+
+#include <vector>
+
+#include <osgEarth/Map>
+#include <osgEarth/MapNode>
+#include <osgEarthAnnotation/FeatureNode>
+#include <osgEarthUtil/Controls>
+#include <osgEarthUtil/ExampleResources>
+//#include <osgEarthUtil/SkyNode>
+
+namespace PackageQt
+{
+  struct BoundsSetCallback : public osg::Referenced
+  {
+    virtual void boundsSet(const osg::Vec2d& ll, const osg::Vec2d& ur) { }
+  };
+
+  class SceneController
+  {
+  public:
+
+    SceneController(osg::Group* root, osgViewer::View* view, const std::string& url="");
+
+    osg::Node* loadEarthFile(const std::string& url);
+
+    osg::Node* earthNode() { return _earthNode.get(); }
+    osgEarth::MapNode* mapNode() { return _mapNode.get(); }
+    osgEarth::Map* map() { return _map.get(); }
+
+    void captureBounds(BoundsSetCallback* callback=0L);
+    void endBoundsCapture();
+    void clearBounds();
+
+    void setBounds(const osgEarth::GeoPoint& p1, const osgEarth::GeoPoint& p2);
+    const osg::Vec2d &getBoundsLL() { return _boundsLL; }
+    const osg::Vec2d &getBoundsUR() { return _boundsUR; }
+    std::string getBoundsString();
+
+  private:
+
+    osg::ref_ptr<osg::Group> _root;
+    osg::ref_ptr<osgViewer::View> _view;
+    osg::ref_ptr<osgEarth::Util::Controls::ControlCanvas> _canvas;
+    osg::ref_ptr<osgEarth::Util::Controls::Container> _controlContainer;
+    osg::ref_ptr<osg::Group> _annoRoot;
+
+    osg::ref_ptr<osg::Node> _earthNode;
+    osg::ref_ptr<osgEarth::MapNode> _mapNode;
+    osg::ref_ptr<osgEarth::Map> _map;
+    //osg::ref_ptr<osgEarth::Util::SkyNode> _sky;
+
+    osgEarth::Symbology::Style _boundsStyle;
+    osg::Vec2d _boundsLL;
+    osg::Vec2d _boundsUR;
+    osg::ref_ptr<osgEarth::Annotation::FeatureNode> _bboxNode;
+    osg::ref_ptr<osgGA::GUIEventHandler> _guiHandler;
+    osg::ref_ptr<BoundsSetCallback> _boundsCallback;
+  };
+}
+
+#endif //TILER_TOOL_SCENECONTROLLER_H
\ No newline at end of file
diff --git a/src/applications/osgearth_package_qt/TMSExporter.cpp b/src/applications/osgearth_package_qt/TMSExporter.cpp
new file mode 100644
index 0000000..c766b14
--- /dev/null
+++ b/src/applications/osgearth_package_qt/TMSExporter.cpp
@@ -0,0 +1,328 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 "TMSExporter.h"
+
+#include <osg/io_utils>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/WriteFile>
+
+#include <osgEarth/Common>
+#include <osgEarth/Map>
+#include <osgEarth/MapNode>
+#include <osgEarth/Registry>
+#include <osgEarth/StringUtils>
+#include <osgEarth/HTTPClient>
+#include <osgEarthUtil/TMSPackager>
+#include <osgEarthDrivers/tms/TMSOptions>
+
+#include <iostream>
+#include <sstream>
+#include <iterator>
+
+using namespace PackageQt;
+using namespace osgEarth;
+using namespace osgEarth::Util;
+using namespace osgEarth::Drivers;
+
+#define LC "[TMSExporter] "
+
+namespace
+{
+  /** Packaging task for a single layer. */
+  struct PackageLayer
+  {
+    void init(osgEarth::Map* map, osgEarth::ImageLayer* layer, osgDB::Options* options, const std::string& rootFolder, const std::string& layerFolder, bool verbose, bool overwrite, bool keepEmpties, unsigned int maxLevel, const std::string& extension, std::vector< osgEarth::Bounds >& bounds)
+    {
+      _map = map;
+      _imageLayer = layer;
+      _options = options;
+      _rootFolder = rootFolder;
+      _layerFolder = layerFolder;
+      _verbose = verbose;
+      _overwrite = overwrite;
+      _keepEmpties = keepEmpties;
+      _maxLevel = maxLevel;
+      _extension = extension;
+      _bounds = bounds;
+      _packageResult.ok = false;
+    }
+
+    void init(osgEarth::Map* map, osgEarth::ElevationLayer* layer, osgDB::Options* options, const std::string& rootFolder, const std::string& layerFolder, bool verbose, bool overwrite, bool keepEmpties, unsigned int maxLevel, const std::string& extension, std::vector< osgEarth::Bounds >& bounds)
+    {
+      _map = map;
+      _elevationLayer = layer;
+      _options = options;
+      _rootFolder = rootFolder;
+      _layerFolder = layerFolder;
+      _verbose = verbose;
+      _overwrite = overwrite;
+      _keepEmpties = keepEmpties;
+      _maxLevel = maxLevel;
+      _extension = extension;
+      _bounds = bounds;
+      _packageResult.ok = false;
+    }
+
+    void execute()
+    {
+      TMSPackager packager( _map->getProfile(), _options);
+
+      packager.setVerbose( _verbose );
+      packager.setOverwrite( _overwrite );
+      packager.setKeepEmptyImageTiles( _keepEmpties );
+
+      if ( _maxLevel != ~0 )
+          packager.setMaxLevel( _maxLevel );
+
+      if (_bounds.size() > 0)
+      {
+          for (unsigned int i = 0; i < _bounds.size(); ++i)
+          {
+              Bounds b = _bounds[i];            
+              if ( b.isValid() )
+                  packager.addExtent( osgEarth::GeoExtent(_map->getProfile()->getSRS(), b) );
+          }
+      }
+
+      std::string layerRoot = osgDB::concatPaths( _rootFolder, _layerFolder );
+
+      if (_imageLayer.valid())
+      {
+        if (_verbose)
+          OE_NOTICE << LC << "Packaging image layer \"" << _layerFolder << "\"" << std::endl;
+
+        _packageResult = packager.package( _imageLayer.get(), layerRoot, _extension );
+      }
+      else if (_elevationLayer.valid())
+      {
+        if (_verbose)
+          OE_NOTICE << LC << "Packaging elevation layer \"" << _layerFolder << "\"" << std::endl;
+
+        _packageResult = packager.package( _elevationLayer.get(), layerRoot );
+      }
+    }
+
+    osg::ref_ptr<osgEarth::Map> _map;
+    osg::ref_ptr<osgEarth::ImageLayer> _imageLayer;
+    osg::ref_ptr<osgEarth::ElevationLayer> _elevationLayer;
+    osg::ref_ptr<osgDB::Options> _options;
+    std::string _rootFolder;
+    std::string _layerFolder;
+    bool _verbose;
+    bool _overwrite;
+    bool _keepEmpties;
+    unsigned int _maxLevel;
+    std::string _extension;
+    std::vector< osgEarth::Bounds > _bounds;
+    TMSPackager::Result _packageResult;
+  };
+}
+
+
+TMSExporter::TMSExporter(const std::string& log)
+: _dbOptions(""), _maxLevel(~0), _keepEmpties(false), _errorMessage("")
+{
+  unsigned num = 2 * OpenThreads::GetNumberOfProcessors();
+  _taskService = new osgEarth::TaskService("TMS Packager", num);
+}
+
+/** Packages image and elevation layers as a TMS. */
+int TMSExporter::exportTMS(MapNode* mapNode, const std::string& path, std::vector< osgEarth::Bounds >& bounds, const std::string& outEarth, bool overwrite, const std::string& extension)
+{
+  if ( !mapNode )
+  {
+    _errorMessage = "Invalid MapNode";
+    if (_progress.valid()) _progress->onCompleted();
+    return 0;
+  }
+
+  // folder to which to write the TMS archive.
+  std::string rootFolder = path;
+
+  osg::ref_ptr<osgDB::Options> options = new osgDB::Options(_dbOptions);
+
+  // create a folder for the output
+  osgDB::makeDirectory(rootFolder);
+  if ( !osgDB::fileExists(rootFolder) )
+  {
+    _errorMessage = "Failed to create root output folder";
+    if (_progress.valid()) _progress->onCompleted();
+    return 0;
+  }
+
+  Map* map = mapNode->getMap();
+
+  // new map for an output earth file if necessary.
+  osg::ref_ptr<Map> outMap = 0L;
+  if ( !outEarth.empty() )
+  {
+      // copy the options from the source map first
+      outMap = new Map(map->getInitialMapOptions());
+  }
+
+  // establish the output path of the earth file, if applicable:
+  std::string outEarthName = osgDB::getSimpleFileName(outEarth);
+  if (outEarthName.length() > 0 && osgEarth::toLower(osgDB::getFileExtension(outEarthName)) != "earth")
+    outEarthName += ".earth";
+
+  std::string outEarthFile = osgDB::concatPaths(rootFolder, outEarthName);
+  
+
+  // semaphore and tasks collection for multithreading
+  osgEarth::Threading::MultiEvent semaphore;
+  osgEarth::TaskRequestVector tasks;
+  int taskCount = 0;
+
+
+  // package any image layers that are enabled and visible
+  ImageLayerVector imageLayers;
+  map->getImageLayers( imageLayers );
+
+  unsigned imageCount = 0;
+  for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++imageCount )
+  {
+      ImageLayer* layer = i->get();
+
+      if ( layer->getEnabled() && layer->getVisible() )
+      {
+          std::string layerFolder = toLegalFileName( layer->getName() );
+          if ( layerFolder.empty() )
+              layerFolder = Stringify() << "image_layer_" << imageCount;
+
+          ParallelTask<PackageLayer>* task = new ParallelTask<PackageLayer>( &semaphore );
+          task->init(map, layer, options, rootFolder, layerFolder, true, overwrite, _keepEmpties, _maxLevel, extension, bounds);
+          task->setProgressCallback(new PackageLayerProgressCallback(this));
+          tasks.push_back(task);
+          taskCount++;
+      }
+  }
+
+  // package any elevation layers that are enabled and visible
+  ElevationLayerVector elevationLayers;
+  map->getElevationLayers( elevationLayers );
+
+  int elevCount = 0;
+  for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++elevCount )
+  {
+      ElevationLayer* layer = i->get();
+      if ( layer->getEnabled() && layer->getVisible() )
+      {
+          std::string layerFolder = toLegalFileName( layer->getName() );
+          if ( layerFolder.empty() )
+              layerFolder = Stringify() << "elevation_layer_" << elevCount;
+
+          ParallelTask<PackageLayer>* task = new ParallelTask<PackageLayer>( &semaphore );
+          task->init(map, layer, options, rootFolder, layerFolder, true, overwrite, _keepEmpties, _maxLevel, extension, bounds);
+          task->setProgressCallback(new PackageLayerProgressCallback(this));
+          tasks.push_back(task);
+          taskCount++;
+      }
+  }
+
+
+  // Run all the tasks in parallel
+  _totalTasks = taskCount;
+  _completedTasks = 0;
+
+  semaphore.reset( _totalTasks );
+
+  for( TaskRequestVector::iterator i = tasks.begin(); i != tasks.end(); ++i )
+        _taskService->add( i->get() );
+
+  // Wait for them to complete
+  semaphore.wait();
+
+
+  // Add successfully packaged layers to the new map object and
+  // write out the .earth file (if requested)
+  if (outMap.valid())
+  {
+    for( TaskRequestVector::iterator i = tasks.begin(); i != tasks.end(); ++i )
+    {
+      PackageLayer* p = dynamic_cast<PackageLayer*>(i->get());
+      if (p)
+      {
+        if (p->_packageResult.ok)
+        {
+          TMSOptions tms;
+          tms.url() = URI(osgDB::concatPaths(p->_layerFolder, "tms.xml"), outEarthFile );
+
+          if (p->_imageLayer.valid())
+          {
+            ImageLayerOptions layerOptions( p->_imageLayer->getName(), tms );
+            layerOptions.mergeConfig( p->_imageLayer->getInitialOptions().getConfig(true) );
+            layerOptions.cachePolicy() = CachePolicy::NO_CACHE;
+
+            outMap->addImageLayer( new ImageLayer(layerOptions) );
+          }
+          else
+          {
+            ElevationLayerOptions layerOptions( p->_elevationLayer->getName(), tms );
+            layerOptions.mergeConfig( p->_elevationLayer->getInitialOptions().getConfig(true) );
+            layerOptions.cachePolicy() = CachePolicy::NO_CACHE;
+
+            outMap->addElevationLayer( new ElevationLayer(layerOptions) );
+          }
+        }
+        else
+        {
+          OE_WARN << LC << p->_packageResult.message << std::endl;
+        }
+      }
+    }
+  }
+
+  if ( outMap.valid() )
+  {
+      MapNodeOptions outNodeOptions = mapNode->getMapNodeOptions();
+      osg::ref_ptr<MapNode> outMapNode = new MapNode(outMap.get(), outNodeOptions);
+      if ( !osgDB::writeNodeFile(*outMapNode.get(), outEarthFile) )
+      {
+          OE_WARN << LC << "Error writing earth file to \"" << outEarthFile << "\"" << std::endl;
+      }
+      else
+      {
+          OE_NOTICE << LC << "Wrote earth file to \"" << outEarthFile << "\"" << std::endl;
+      }
+  }
+
+
+  // Mark the progress callback as completed
+  if (_progress.valid()) _progress->onCompleted();
+
+  return elevCount + imageCount;
+}
+
+void TMSExporter::packageTaskComplete()
+{
+  OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _m );
+
+  _completedTasks++;
+
+  //if ( _progress.valid() && (_progress->isCanceled() || _progress->reportProgress(_completedTasks, _totalTasks)) )
+  //{
+  //    //Canceled
+  //}
+
+  if (_progress.valid())
+    _progress->reportProgress(_completedTasks, _totalTasks);
+}
+
diff --git a/src/applications/osgearth_package_qt/TMSExporter.h b/src/applications/osgearth_package_qt/TMSExporter.h
new file mode 100644
index 0000000..a09ebee
--- /dev/null
+++ b/src/applications/osgearth_package_qt/TMSExporter.h
@@ -0,0 +1,127 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 TILER_TOOL_TMSEXPORTER_H
+#define TILER_TOOL_TMSEXPORTER_H 1
+
+#include <vector>
+
+#include <osgEarth/Map>
+#include <osgEarth/MapNode>
+#include <osgEarth/Progress>
+#include <osgEarth/TaskService>
+
+namespace PackageQt
+{
+  class PackageLayerProgressCallback;
+
+  class TMSExporter
+  {
+  public:
+
+    TMSExporter(const std::string& log="log.txt");
+
+    int exportTMS(osgEarth::MapNode* mapNode, const std::string& path, std::vector< osgEarth::Bounds >& bounds, const std::string& outEarth="", bool overwrite=false, const std::string& extension="");
+
+    std::string getDBOptions() { return _dbOptions; }
+    void setDBOptions(const std::string& options) { _dbOptions = options; }
+    unsigned getMaxLevel() { return _maxLevel; }
+    void setMaxLevel(unsigned level) { _maxLevel = level; }
+
+    bool getKeepEmpties() { return _keepEmpties; }
+    void setKeepEmpties(bool keep) { _keepEmpties = keep; }
+
+    std::string getErrorMessage() { return _errorMessage; }
+
+    void setProgressCallback(osgEarth::ProgressCallback* progress) { _progress = progress ? progress : new osgEarth::ProgressCallback; }
+
+  protected:
+    friend class PackageLayerProgressCallback;
+
+    void packageTaskComplete();
+
+  private:
+
+    std::string _dbOptions;
+    unsigned _maxLevel;
+    bool _keepEmpties;
+    std::string _errorMessage;
+
+    OpenThreads::Mutex _m;
+
+    osg::ref_ptr<osgEarth::TaskService> _taskService;
+    int _totalTasks;
+    int _completedTasks;
+    osg::ref_ptr<osgEarth::ProgressCallback> _progress;
+  };
+
+
+  class TMSExporterWorkerThread : public osg::Referenced, public OpenThreads::Thread
+  {
+  public:
+    TMSExporterWorkerThread(TMSExporter* exporter, osgEarth::MapNode* mapNode, const std::string& path, std::vector< osgEarth::Bounds >& bounds, const std::string& outEarth="", bool overwrite=false, const std::string& extension="")
+      : OpenThreads::Thread(), _exporter(exporter), _mapNode(mapNode), _path(path), _bounds(bounds), _outEarth(outEarth), _overwrite(overwrite), _extension(extension)
+    { }
+
+    void run()
+    {
+      if (_exporter)
+      {
+        _exporter->exportTMS(_mapNode, _path, _bounds, _outEarth, _overwrite, _extension);
+      }
+    }
+
+  private:
+    TMSExporter* _exporter;
+    osg::ref_ptr<osgEarth::MapNode> _mapNode;
+    std::string _path;
+    std::vector< osgEarth::Bounds > _bounds;
+    std::string _outEarth;
+    bool _overwrite;
+    std::string _extension;
+  };
+
+
+  class PackageLayerProgressCallback : public osgEarth::ProgressCallback
+  {
+  public:
+    PackageLayerProgressCallback(TMSExporter* exporter)
+      : _exporter(exporter)
+    {
+    }
+
+    virtual ~PackageLayerProgressCallback() { }
+
+    bool reportProgress(double current, double total, unsigned currentStage, unsigned totalStages, const std::string& msg)
+    {
+      return false;
+    }
+
+    void onCompleted()
+    {
+      if (_exporter)
+        _exporter->packageTaskComplete();
+    }
+
+  private:
+    TMSExporter* _exporter;
+  };
+}
+
+#endif //TILER_TOOL_TMSEXPORTER_H
\ No newline at end of file
diff --git a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp b/src/applications/osgearth_package_qt/WaitDialog
similarity index 71%
copy from src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
copy to src/applications/osgearth_package_qt/WaitDialog
index 4de5916..fdf727b 100644
--- a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
+++ b/src/applications/osgearth_package_qt/WaitDialog
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,16 +16,24 @@
 * 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 "KeyNodeFactory"
 
-using namespace osgEarth_engine_osgterrain;
-using namespace osgEarth;
+#ifndef TILER_TOOL_WAITDIALOG_H
+#define TILER_TOOL_WAITDIALOG_H
 
-#define LC "[KeyNodeFactory] "
+#include <QDialog>
+#include <QLabel>
 
-//--------------------------------------------------------------------------
+namespace PackageQt
+{
 
-KeyNodeFactory::KeyNodeFactory()
+class WaitDialog : public QDialog
 {
-    //NOP
+  Q_OBJECT
+
+public:
+  WaitDialog(const QString &message, QWidget *parent = 0);
+};
+
 }
+
+#endif //TILER_TOOL_WAITDIALOG_H
\ No newline at end of file
diff --git a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp b/src/applications/osgearth_package_qt/WaitDialog.cpp
similarity index 56%
copy from src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
copy to src/applications/osgearth_package_qt/WaitDialog.cpp
index 4de5916..11da2bc 100644
--- a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
+++ b/src/applications/osgearth_package_qt/WaitDialog.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,16 +16,24 @@
 * 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 "KeyNodeFactory"
 
-using namespace osgEarth_engine_osgterrain;
-using namespace osgEarth;
+#include "WaitDialog"
 
-#define LC "[KeyNodeFactory] "
+#include <QDialog>
+#include <QLabel>
+#include <QVBoxLayout>
 
-//--------------------------------------------------------------------------
+using namespace PackageQt;
 
-KeyNodeFactory::KeyNodeFactory()
+WaitDialog::WaitDialog(const QString &message, QWidget *parent)
+  : QDialog(parent, Qt::CustomizeWindowHint|Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint)
 {
-    //NOP
+  setStyleSheet("background-color: #666; color: #fff; font-size: 18px;");
+
+  QLabel* msgLabel = new QLabel(message);
+
+  QVBoxLayout* mainLayout = new QVBoxLayout;
+  mainLayout->setContentsMargins(30, 30, 30, 30);
+  mainLayout->addWidget(msgLabel);
+  setLayout(mainLayout);
 }
diff --git a/src/applications/osgearth_package_qt/data/base.earth b/src/applications/osgearth_package_qt/data/base.earth
new file mode 100644
index 0000000..620ad2a
--- /dev/null
+++ b/src/applications/osgearth_package_qt/data/base.earth
@@ -0,0 +1,11 @@
+<!-- 
+Simple basemap
+-->
+
+<map name="simple_basemap" type="geocentric" version="2">
+
+    <image driver="gdal" name="world-tiff">
+        <url>./world.tif</url>
+    </image>
+    
+</map>
diff --git a/src/applications/osgearth_package_qt/data/world.dbf b/src/applications/osgearth_package_qt/data/world.dbf
new file mode 100644
index 0000000..86d3dff
Binary files /dev/null and b/src/applications/osgearth_package_qt/data/world.dbf differ
diff --git a/src/applications/osgearth_package_qt/data/world.prj b/src/applications/osgearth_package_qt/data/world.prj
new file mode 100644
index 0000000..76ce7eb
--- /dev/null
+++ b/src/applications/osgearth_package_qt/data/world.prj
@@ -0,0 +1 @@
+GEOGCS["Geographic Coordinate System",DATUM["WGS84",SPHEROID["WGS84",6378137,298.257223560493]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]
diff --git a/src/applications/osgearth_package_qt/data/world.shp b/src/applications/osgearth_package_qt/data/world.shp
new file mode 100644
index 0000000..7b026fd
Binary files /dev/null and b/src/applications/osgearth_package_qt/data/world.shp differ
diff --git a/src/applications/osgearth_package_qt/data/world.shx b/src/applications/osgearth_package_qt/data/world.shx
new file mode 100644
index 0000000..b4792d3
Binary files /dev/null and b/src/applications/osgearth_package_qt/data/world.shx differ
diff --git a/data/world.tif b/src/applications/osgearth_package_qt/data/world.tif
similarity index 100%
copy from data/world.tif
copy to src/applications/osgearth_package_qt/data/world.tif
diff --git a/src/applications/osgearth_package_qt/images.qrc b/src/applications/osgearth_package_qt/images.qrc
new file mode 100644
index 0000000..96d85a8
--- /dev/null
+++ b/src/applications/osgearth_package_qt/images.qrc
@@ -0,0 +1,14 @@
+<RCC>
+    <qresource prefix="/">
+        <file>images/add_elevation.png</file>
+        <file>images/add_image.png</file>
+        <file>images/earth.png</file>
+        <file>images/save.png</file>
+        <file>images/delete.png</file>
+        <file>images/export.png</file>
+        <file>images/bbox.png</file>
+        <file>images/bbox_clear.png</file>
+        <file>images/add_elevation_folder.png</file>
+        <file>images/add_image_folder.png</file>
+    </qresource>
+</RCC>
diff --git a/src/applications/osgearth_package_qt/images/add_elevation.png b/src/applications/osgearth_package_qt/images/add_elevation.png
new file mode 100644
index 0000000..3fdbe1e
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/add_elevation.png differ
diff --git a/src/applications/osgearth_package_qt/images/add_elevation_folder.png b/src/applications/osgearth_package_qt/images/add_elevation_folder.png
new file mode 100644
index 0000000..3f46ba9
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/add_elevation_folder.png differ
diff --git a/src/applications/osgearth_package_qt/images/add_image.png b/src/applications/osgearth_package_qt/images/add_image.png
new file mode 100644
index 0000000..6c8ef49
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/add_image.png differ
diff --git a/src/applications/osgearth_package_qt/images/add_image_folder.png b/src/applications/osgearth_package_qt/images/add_image_folder.png
new file mode 100644
index 0000000..4e2b026
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/add_image_folder.png differ
diff --git a/src/applications/osgearth_package_qt/images/bbox.png b/src/applications/osgearth_package_qt/images/bbox.png
new file mode 100644
index 0000000..718180d
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/bbox.png differ
diff --git a/src/applications/osgearth_package_qt/images/bbox_clear.png b/src/applications/osgearth_package_qt/images/bbox_clear.png
new file mode 100644
index 0000000..b1476f9
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/bbox_clear.png differ
diff --git a/src/applications/osgearth_package_qt/images/delete.png b/src/applications/osgearth_package_qt/images/delete.png
new file mode 100644
index 0000000..c64d0fb
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/delete.png differ
diff --git a/src/applications/osgearth_package_qt/images/earth.png b/src/applications/osgearth_package_qt/images/earth.png
new file mode 100644
index 0000000..388c13d
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/earth.png differ
diff --git a/src/applications/osgearth_package_qt/images/export.png b/src/applications/osgearth_package_qt/images/export.png
new file mode 100644
index 0000000..2d60f39
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/export.png differ
diff --git a/src/applications/osgearth_package_qt/images/save.png b/src/applications/osgearth_package_qt/images/save.png
new file mode 100644
index 0000000..666a218
Binary files /dev/null and b/src/applications/osgearth_package_qt/images/save.png differ
diff --git a/src/applications/osgearth_package_qt/package_qt.cpp b/src/applications/osgearth_package_qt/package_qt.cpp
new file mode 100644
index 0000000..b438604
--- /dev/null
+++ b/src/applications/osgearth_package_qt/package_qt.cpp
@@ -0,0 +1,129 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osg/Notify>
+#include <osgDB/FileUtils>
+#include <osgEarth/ImageUtils>
+#include <osgEarth/MapNode>
+#include <osgEarthQt/ViewerWidget>
+#include <osgEarthQt/LayerManagerWidget>
+#include <osgEarthQt/MapCatalogWidget>
+#include <osgEarthQt/DataManager>
+#include <osgEarthUtil/EarthManipulator>
+
+#include <QtGui/QApplication>
+
+#include "PackageQtMainWindow"
+#include "SceneController.h"
+#include "TMSExporter.h"
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#endif
+
+using namespace PackageQt;
+using namespace osgEarth::Util;
+using namespace osgEarth::Util::Controls;
+
+
+/** Finds an argument with the specified extension. */
+std::string
+findArgumentWithExtension( osg::ArgumentParser& args, const std::string& ext )
+{
+    for( int i=0; i<args.argc(); ++i )
+    {
+        std::string arg( args.argv()[i] );
+        if ( endsWith( toLower(trim(arg)), ".earth" ) )
+            return arg;
+    }
+    return "";
+}
+
+int main(int argc, char** argv)
+{
+  HTTPClient::setUserAgent( "osgearth_package_qt/1.0" );
+
+  //setup log file
+  char *appData = getenv("APPDATA");
+
+  std::string logDir = std::string(appData) + "\\osgEarthPackageQt";
+  if (!osgDB::fileExists(logDir))
+    osgDB::makeDirectory(logDir);
+
+  std::string logPath = logDir + "\\log.txt";
+  std::ofstream* log = new std::ofstream( logPath.c_str() );
+  std::cout.rdbuf( log->rdbuf() );
+  std::cerr.rdbuf( log->rdbuf() );
+
+  if (getenv("OSGEARTH_PACKAGE_LOGGING") != 0)
+  {
+    std::string level( getenv("OSGEARTH_PACKAGE_LOGGING") );
+    if ( level == "INFO" )
+      osgEarth::setNotifyLevel( osg::INFO );
+    else if ( level == "DEBUG" )
+      osgEarth::setNotifyLevel( osg::DEBUG_INFO );
+  }
+  else
+  {
+    osgEarth::setNotifyLevel( osg::INFO );
+  }
+
+  osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);
+
+  #ifdef Q_WS_X11
+  XInitThreads();
+  #endif
+
+  QApplication app(argc, argv);
+
+  osg::ref_ptr<osg::Group> root = new osg::Group();
+
+  //create ViewWidget and get views collection
+  osgEarth::QtGui::ViewerWidget* viewerWidget = new osgEarth::QtGui::ViewerWidget( root );
+  osgEarth::QtGui::ViewVector views;
+  viewerWidget->getViews( views );
+
+  osg::ref_ptr<osgViewer::View> mainView;
+  if (views.size() > 0)
+    mainView = views[0];
+
+  //create the SceneController, if no earth file is specified a blank
+  //globe will be loaded
+  osg::ArgumentParser args(&argc,argv);
+  std::string earthFile = findArgumentWithExtension(args, ".earth");
+  SceneController controller(root, mainView, earthFile);
+
+  //create the TMSExporter and main window
+  TMSExporter exporter;
+  PackageQtMainWindow appWin(viewerWidget, &controller, &exporter);
+  appWin.setGeometry(100, 100, 1280, 800);
+  appWin.show();
+
+  //return app.exec();
+  int ret = app.exec();
+
+  //TODO: move somewhere smarter
+  if (log)
+  {
+      log->close();
+      delete log;
+  }
+
+  return ret;
+}
diff --git a/src/applications/osgearth_qt/DemoMainWindow b/src/applications/osgearth_qt/DemoMainWindow
index c063ef4..2e5cee2 100644
--- a/src/applications/osgearth_qt/DemoMainWindow
+++ b/src/applications/osgearth_qt/DemoMainWindow
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_qt/osgearth_qt.cpp b/src/applications/osgearth_qt/osgearth_qt.cpp
index 98a3a41..a4812f0 100644
--- a/src/applications/osgearth_qt/osgearth_qt.cpp
+++ b/src/applications/osgearth_qt/osgearth_qt.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_qt_simple/osgearth_qt_simple.cpp b/src/applications/osgearth_qt_simple/osgearth_qt_simple.cpp
index 2431ea4..cebb296 100644
--- a/src/applications/osgearth_qt_simple/osgearth_qt_simple.cpp
+++ b/src/applications/osgearth_qt_simple/osgearth_qt_simple.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_qt_windows/CMakeLists.txt b/src/applications/osgearth_qt_windows/CMakeLists.txt
new file mode 100644
index 0000000..07aba8d
--- /dev/null
+++ b/src/applications/osgearth_qt_windows/CMakeLists.txt
@@ -0,0 +1,28 @@
+INCLUDE( ${QT_USE_FILE} )
+
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} ${QT_INCLUDES})
+
+SET(MOC_HDRS
+)
+
+QT4_WRAP_CPP( MOC_SRCS ${MOC_HDRS} OPTIONS "-f" )
+
+SET(TARGET_H
+    ${LIB_QT_RCS}
+)
+
+SET(TARGET_SRC
+    ${MOC_SRCS}
+    ${LIB_RC_SRCS}
+    osgearth_qt_windows.cpp
+)
+
+SET(TARGET_ADDED_LIBRARIES
+    osgEarthQt
+    ${QT_QTCORE_LIBRARY}
+    ${QT_QTGUI_LIBRARY}
+    ${QT_QTOPENGL_LIBRARY}
+)
+
+#### end var setup  ###
+SETUP_APPLICATION(osgearth_qt_windows)
diff --git a/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp b/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp
new file mode 100644
index 0000000..3f3f328
--- /dev/null
+++ b/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp
@@ -0,0 +1,170 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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/>
+*/
+
+
+/**
+ * This demo show how to create multiple Qt widgets/windows and to share
+ * the map amongst them.
+ */
+
+#include <osg/Notify>
+#include <osgViewer/CompositeViewer>
+#include <osgEarthUtil/EarthManipulator>
+#include <osgEarthQt/ViewWidget>
+#include <QtGui/QApplication>
+#include <QtGui/QDialog>
+#include <QtGui/QMainWindow>
+#include <QtGui/QPushButton>
+#include <QtGui/QLayout>
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#endif
+
+using namespace osgEarth;
+using namespace osgEarth::Util;
+using namespace osgEarth::QtGui;
+
+//------------------------------------------------------------------
+
+int
+usage( const std::string& msg, osg::ArgumentParser& args ) 
+{
+    OE_NOTICE << msg << std::endl << std::endl;
+    OE_NOTICE << "USAGE: " << args[0] << " file.earth" << std::endl;
+        
+    return -1;
+}
+
+//------------------------------------------------------------------
+
+struct ViewController 
+{
+    virtual void addView() =0;
+};
+
+
+struct MyMainWindow : public QMainWindow, public ViewController
+{
+    QTimer                     _timer;
+    osgViewer::CompositeViewer _viewer;
+    osg::ref_ptr<osg::Node>    _scene;
+
+    MyMainWindow(osg::ArgumentParser& args, osg::Node* scene) 
+        : _viewer(args), _scene(scene)
+    {
+        // we have to add a starting view, otherwise the CV will automatically
+        // mark itself as done :/
+        addView();
+
+        // timer fires a paint event.
+        connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
+        _timer.start(20);
+    }
+
+    void paintEvent(QPaintEvent* e)
+    {
+        // refresh all the views.
+        if (_viewer.getRunFrameScheme() == osgViewer::ViewerBase::CONTINUOUS || 
+            _viewer.checkNeedToDoFrame() )
+        {
+            _viewer.frame();
+        }
+    }
+
+    void addView()
+    {
+        // the new View we want to add:
+        osgViewer::View* view = new osgViewer::View();
+
+        // a widget to hold our view:
+        QWidget* viewWidget = new osgEarth::QtGui::ViewWidget(view);
+
+        // a dialog to hold the view widget:
+        QDialog* win = new QDialog(this);
+        win->setModal( false );
+        win->setLayout( new QHBoxLayout() );
+        win->layout()->addWidget( viewWidget );
+        int x = osgEarth::Random().next( 1024 );
+        int y = osgEarth::Random().next( 768 );
+        win->setGeometry( x, y, 640, 480 );
+        win->show();
+
+        // set up the view
+        view->setCameraManipulator( new osgEarth::Util::EarthManipulator );
+        view->setSceneData( _scene.get() );  
+        view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true,false);
+
+        // add it to the composite viewer.
+        _viewer.addView( view );
+    }
+};
+
+//------------------------------------------------------------------
+
+struct AddButton : public QPushButton
+{
+    ViewController* _vc;
+
+    AddButton(ViewController* vc, const char* text)
+        : QPushButton(text), _vc(vc) { }
+
+    void mousePressEvent( QMouseEvent* e )
+    {
+        _vc->addView();
+    }
+};
+
+//------------------------------------------------------------------
+
+int
+main(int argc, char** argv)
+{
+    osg::ArgumentParser args(&argc,argv);
+    if ( args.find("--help") >= 0)
+        return usage("Help", args);
+
+    // load something
+    osg::Node* node = osgDB::readNodeFiles( args );
+    if ( !node )
+        return usage("Can't load a scene!", args);
+
+
+#ifdef Q_WS_X11
+    // required for multi-threaded viewer on linux:
+    XInitThreads();
+#endif
+
+    // Qt setup:
+    QApplication q(argc, argv);
+
+    // fire up our controller:
+    MyMainWindow win( args, node );
+
+    // A button for adding new views:
+    QPushButton* addButton = new AddButton( &win, "Add a view" );
+
+    // Main window for the button.
+    //QMainWindow win;
+    win.setCentralWidget( addButton );
+    win.setGeometry( 100, 100, 200, 100 );
+    win.show();
+
+    q.exec();
+}
diff --git a/src/applications/osgearth_seed/osgearth_seed.cpp b/src/applications/osgearth_seed/osgearth_seed.cpp
index 318a030..c52aab4 100644
--- a/src/applications/osgearth_seed/osgearth_seed.cpp
+++ b/src/applications/osgearth_seed/osgearth_seed.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_sequencecontrol/CMakeLists.txt b/src/applications/osgearth_sequencecontrol/CMakeLists.txt
new file mode 100644
index 0000000..a89833f
--- /dev/null
+++ b/src/applications/osgearth_sequencecontrol/CMakeLists.txt
@@ -0,0 +1,7 @@
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
+SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
+
+SET(TARGET_SRC osgearth_sequencecontrol.cpp )
+
+#### end var setup  ###
+SETUP_APPLICATION(osgearth_sequencecontrol)
diff --git a/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp b/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp
new file mode 100644
index 0000000..ee4bc15
--- /dev/null
+++ b/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp
@@ -0,0 +1,110 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osgViewer/Viewer>
+#include <osgEarth/MapNode>
+#include <osgEarth/TimeControl>
+#include <osgEarthUtil/ExampleResources>
+#include <osgEarthUtil/EarthManipulator>
+
+namespace ui = osgEarth::Util::Controls;
+
+
+int
+usage(char** argv, const char* msg)
+{
+    OE_WARN << argv[0] << ": " << msg << std::endl;
+    return -1;
+}
+
+
+// Updates the timestamp label once per frame.
+struct UpdateLabel : public osg::Operation
+{
+    osgEarth::SequenceControl* _sc;
+    ui::LabelControl*          _label;
+    unsigned                   _prevIndex;
+
+    UpdateLabel(osgEarth::SequenceControl* sc, ui::LabelControl* label)
+        : osg::Operation("updatelabel", true), _sc(sc), _label(label), _prevIndex(INT_MAX) { }
+
+    void operator()(osg::Object* obj)
+    {
+        osgViewer::View* view = dynamic_cast<osgViewer::View*>(obj);
+        unsigned index = _sc->getCurrentSequenceFrameIndex(view->getFrameStamp());
+        if ( index >= 0 && index != _prevIndex )
+        {
+            const std::vector<osgEarth::SequenceFrameInfo>& frames = _sc->getSequenceFrameInfo();
+            _label->setText( frames[index].timeIdentifier );
+            _prevIndex = index;
+        }
+    }
+};
+
+
+int
+main(int argc, char** argv)
+{
+    osg::ArgumentParser arguments(&argc,argv);
+    osgViewer::Viewer viewer(arguments);
+    viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
+
+    // load an earth file from the command line.
+    osg::Node* node = osgEarth::Util::MapNodeHelper().load( arguments, &viewer );
+    osgEarth::MapNode* mapNode = osgEarth::MapNode::get(node);
+    if ( !mapNode )
+        return usage(argv, "Unable to load an earth file.");
+    viewer.setSceneData( node );
+
+    // find the first layer with sequence control.
+    bool found = false;
+    osgEarth::ImageLayerVector layers;
+    mapNode->getMap()->getImageLayers( layers );
+    for( osgEarth::ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
+    {
+        // get the sequence control interface for the layer, if there is one.
+        osgEarth::ImageLayer*      layer = i->get();
+        osgEarth::SequenceControl* sc = layer->getSequenceControl();
+        if ( sc )
+        {
+            // found one, so make a label for it:
+            ui::LabelControl* label = new ui::LabelControl("Time");
+            label->setFontSize( 24.0f );
+            label->setBackColor ( 0, 0, 0, 0.5 );
+            label->setHorizAlign( ui::Control::ALIGN_CENTER );
+            label->setVertAlign ( ui::Control::ALIGN_TOP );
+            ui::ControlCanvas::get( &viewer )->addControl( label );
+
+            // make sure the sequence is playing:
+            sc->playSequence();
+
+            // add a callback to update the label with the sequence time id
+            viewer.addUpdateOperation( new UpdateLabel(sc, label) );
+            found = true;
+            break;
+        }
+    }
+
+    if (!found)
+    {
+        return usage(argv, "Your earth file does not contain any sequenced layers...bye!");
+    }
+
+    return viewer.run();
+}
diff --git a/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp b/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
index 92b0c21..b32e289 100644
--- a/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
+++ b/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -60,10 +60,9 @@ namespace TEST_1
     char s_hazeVertShader[] =
         "#version " GLSL_VERSION_STR "\n"
         "varying vec3 v_pos; \n"
-        "uniform mat4 gl_ModelViewMatrix; \n"
-        "void setup_haze() \n"
+        "void setup_haze(inout vec4 VertexVIEW) \n"
         "{ \n"
-        "    v_pos = vec3(gl_ModelViewMatrix * gl_Vertex); \n"
+        "    v_pos = vec3(VertexVIEW); \n"
         "} \n";
 
     char s_hazeFragShader[] =
@@ -78,8 +77,8 @@ namespace TEST_1
     osg::StateAttribute* createHaze()
     {
         osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
-        vp->setFunction( "setup_haze", s_hazeVertShader, osgEarth::ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
-        vp->setFunction( "apply_haze", s_hazeFragShader, osgEarth::ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING );
+        vp->setFunction( "setup_haze", s_hazeVertShader, osgEarth::ShaderComp::LOCATION_VERTEX_VIEW );
+        vp->setFunction( "apply_haze", s_hazeFragShader, osgEarth::ShaderComp::LOCATION_FRAGMENT_LIGHTING );
         return vp;
     }
 
@@ -137,12 +136,11 @@ namespace TEST_5
 
 #if 0
         osg::Program* p = new osg::Program();
-        p->addShader( new osg::Shader(osg::Shader::VERTEX, s_vert) );
+        p->addShader( new osg::Shader(osg::Shader::VERTEX,   s_vert) );
         p->addShader( new osg::Shader(osg::Shader::FRAGMENT, s_frag) );
 #else
         VirtualProgram* p = new VirtualProgram();
-        p->installDefaultColoringAndLightingShaders();
-        p->setFunction("test", s_vp, ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING);
+        p->setFunction("test", s_vp, ShaderComp::LOCATION_FRAGMENT_LIGHTING);
 #endif
 
         n1->getOrCreateStateSet()->setAttributeAndModes( p, 1 );
diff --git a/src/applications/osgearth_shadow/osgearth_shadow.cpp b/src/applications/osgearth_shadow/osgearth_shadow.cpp
index 6b1d188..6f4070a 100644
--- a/src/applications/osgearth_shadow/osgearth_shadow.cpp
+++ b/src/applications/osgearth_shadow/osgearth_shadow.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp b/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
index 09a61b8..cdd4ea5 100644
--- a/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
+++ b/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -312,6 +312,7 @@ public:
           ls->tessellation() = 20;
 
           style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+          style.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
 
           feature->style() = style;
 
diff --git a/src/applications/osgearth_tfs/osgearth_tfs.cpp b/src/applications/osgearth_tfs/osgearth_tfs.cpp
index 64de78b..cbcb3f4 100644
--- a/src/applications/osgearth_tfs/osgearth_tfs.cpp
+++ b/src/applications/osgearth_tfs/osgearth_tfs.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_tilesource/osgearth_tilesource.cpp b/src/applications/osgearth_tilesource/osgearth_tilesource.cpp
index abb37da..2f6adea 100644
--- a/src/applications/osgearth_tilesource/osgearth_tilesource.cpp
+++ b/src/applications/osgearth_tilesource/osgearth_tilesource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_toc/osgearth_toc.cpp b/src/applications/osgearth_toc/osgearth_toc.cpp
index 5a881fe..6a1ef2b 100644
--- a/src/applications/osgearth_toc/osgearth_toc.cpp
+++ b/src/applications/osgearth_toc/osgearth_toc.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_tracks/osgearth_tracks.cpp b/src/applications/osgearth_tracks/osgearth_tracks.cpp
index c96c82c..a2eb227 100644
--- a/src/applications/osgearth_tracks/osgearth_tracks.cpp
+++ b/src/applications/osgearth_tracks/osgearth_tracks.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_version/osgearth_version.cpp b/src/applications/osgearth_version/osgearth_version.cpp
index 173d30f..ddd9bb2 100644
--- a/src/applications/osgearth_version/osgearth_version.cpp
+++ b/src/applications/osgearth_version/osgearth_version.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp b/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp
index 779cf9e..50022cc 100644
--- a/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp
+++ b/src/applications/osgearth_verticalscale/osgearth_verticalscale.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -51,13 +51,12 @@ const char* vertexShader =
     "attribute vec4  osgearth_elevData; \n"
     "uniform   float verticalScale;     \n"
 
-    "void applyVerticalScale() \n"
+    "void applyVerticalScale(inout vec4 VertexMODEL) \n"
     "{ \n"
-    "    vec3  upVector = osgearth_elevData.xyz;                     \n"
-    "    float elev     = osgearth_elevData.w;                       \n"
-    "    vec3  offset   = upVector * elev * (verticalScale - 1.0);   \n"
-    "    vec4  vertex   = gl_Vertex + vec4(offset/gl_Vertex.w, 0.0); \n"
-    "    gl_Position    = gl_ModelViewProjectionMatrix * vertex;     \n"
+    "    vec3  upVector = osgearth_elevData.xyz;                   \n"
+    "    float elev     = osgearth_elevData.w;                     \n"
+    "    vec3  offset   = upVector * elev * (verticalScale - 1.0); \n"
+    "    VertexMODEL   += vec4(offset/VertexMODEL.w, 0.0); \n"
     "} \n";
 
 
@@ -69,8 +68,7 @@ osg::StateSet* createStateSet()
     // Install the shaders. We also bind osgEarth's elevation data attribute, which the 
     // terrain engine automatically generates at the specified location.
     VirtualProgram* vp = new VirtualProgram();
-    vp->installDefaultColoringAndLightingShaders();
-    vp->setFunction( "applyVerticalScale", vertexShader, ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
+    vp->setFunction( "applyVerticalScale", vertexShader, ShaderComp::LOCATION_VERTEX_MODEL );
     vp->addBindAttribLocation( "osgearth_elevData", osg::Drawable::ATTRIBUTE_6 );
     stateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
 
diff --git a/src/applications/osgearth_viewer/osgearth_viewer.cpp b/src/applications/osgearth_viewer/osgearth_viewer.cpp
index b7e38d7..2c1363e 100644
--- a/src/applications/osgearth_viewer/osgearth_viewer.cpp
+++ b/src/applications/osgearth_viewer/osgearth_viewer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -21,11 +21,13 @@
 #include <osgViewer/Viewer>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
+#include <osgEarthAnnotation/ModelNode>
 
 #define LC "[viewer] "
 
 using namespace osgEarth;
 using namespace osgEarth::Util;
+using namespace osgEarth::Annotation;
 
 int
 main(int argc, char** argv)
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
index 23e708c..0173b61 100644
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
@@ -12,6 +12,8 @@
 		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 */; };
+		904E22771691CC42002D66FD /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 904E22751691CC42002D66FD /* Accelerate.framework */; };
+		904E22781691CC42002D66FD /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 904E22761691CC42002D66FD /* MobileCoreServices.framework */; };
 		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 */; };
@@ -177,7 +179,9 @@
 		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>"; };
+		9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tiff.a; path = "../../../../osg-ios/lib/libosgdb_tiff.a"; sourceTree = "<group>"; };
+		904E22751691CC42002D66FD /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
+		904E22761691CC42002D66FD /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
 		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; };
@@ -200,99 +204,99 @@
 		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>"; };
+		907D02DC15B86F8700575110 /* libOpenThreads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libOpenThreads.a; path = "../../../../osg-ios/lib/libOpenThreads.a"; sourceTree = "<group>"; };
+		907D02DD15B86F8700575110 /* libosg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosg.a; path = "../../../../osg-ios/lib/libosg.a"; sourceTree = "<group>"; };
+		907D02DE15B86F8700575110 /* libosgAnimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgAnimation.a; path = "../../../../osg-ios/lib/libosgAnimation.a"; sourceTree = "<group>"; };
+		907D02DF15B86F8700575110 /* libosgdb_3dc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_3dc.a; path = "../../../../osg-ios/lib/libosgdb_3dc.a"; sourceTree = "<group>"; };
+		907D02E015B86F8700575110 /* libosgdb_3ds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_3ds.a; path = "../../../../osg-ios/lib/libosgdb_3ds.a"; sourceTree = "<group>"; };
+		907D02E115B86F8700575110 /* libosgdb_ac.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ac.a; path = "../../../../osg-ios/lib/libosgdb_ac.a"; sourceTree = "<group>"; };
+		907D02E215B86F8700575110 /* libosgdb_bmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bmp.a; path = "../../../../osg-ios/lib/libosgdb_bmp.a"; sourceTree = "<group>"; };
+		907D02E315B86F8700575110 /* libosgdb_bsp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bsp.a; path = "../../../../osg-ios/lib/libosgdb_bsp.a"; sourceTree = "<group>"; };
+		907D02E415B86F8700575110 /* libosgdb_bvh.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bvh.a; path = "../../../../osg-ios/lib/libosgdb_bvh.a"; sourceTree = "<group>"; };
+		907D02E515B86F8700575110 /* libosgdb_cfg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_cfg.a; path = "../../../../osg-ios/lib/libosgdb_cfg.a"; sourceTree = "<group>"; };
+		907D02E615B86F8700575110 /* libosgdb_curl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_curl.a; path = "../../../../osg-ios/lib/libosgdb_curl.a"; sourceTree = "<group>"; };
+		907D02E715B86F8700575110 /* libosgdb_dds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dds.a; path = "../../../../osg-ios/lib/libosgdb_dds.a"; sourceTree = "<group>"; };
+		907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osg.a; path = "../../../../osg-ios/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/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/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/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/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/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/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/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/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/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/lib/libosgdb_deprecated_osgwidget.a"; sourceTree = "<group>"; };
+		907D02F315B86F8700575110 /* libosgdb_dot.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dot.a; path = "../../../../osg-ios/lib/libosgdb_dot.a"; sourceTree = "<group>"; };
+		907D02F415B86F8700575110 /* libosgdb_dw.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dw.a; path = "../../../../osg-ios/lib/libosgdb_dw.a"; sourceTree = "<group>"; };
+		907D02F515B86F8700575110 /* libosgdb_dxf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dxf.a; path = "../../../../osg-ios/lib/libosgdb_dxf.a"; sourceTree = "<group>"; };
+		907D02F615B86F8700575110 /* libosgdb_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_freetype.a; path = "../../../../osg-ios/lib/libosgdb_freetype.a"; sourceTree = "<group>"; };
+		907D02F715B86F8700575110 /* libosgdb_gdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_gdal.a; path = "../../../../osg-ios/lib/libosgdb_gdal.a"; sourceTree = "<group>"; };
+		907D02F815B86F8700575110 /* libosgdb_geo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_geo.a; path = "../../../../osg-ios/lib/libosgdb_geo.a"; sourceTree = "<group>"; };
+		907D02F915B86F8700575110 /* libosgdb_glsl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_glsl.a; path = "../../../../osg-ios/lib/libosgdb_glsl.a"; sourceTree = "<group>"; };
+		907D02FA15B86F8700575110 /* libosgdb_gz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_gz.a; path = "../../../../osg-ios/lib/libosgdb_gz.a"; sourceTree = "<group>"; };
+		907D02FB15B86F8700575110 /* libosgdb_hdr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_hdr.a; path = "../../../../osg-ios/lib/libosgdb_hdr.a"; sourceTree = "<group>"; };
+		907D02FC15B86F8700575110 /* libosgdb_imageio.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_imageio.a; path = "../../../../osg-ios/lib/libosgdb_imageio.a"; sourceTree = "<group>"; };
+		907D02FD15B86F8700575110 /* libosgdb_ive.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ive.a; path = "../../../../osg-ios/lib/libosgdb_ive.a"; sourceTree = "<group>"; };
+		907D02FE15B86F8700575110 /* libosgdb_logo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_logo.a; path = "../../../../osg-ios/lib/libosgdb_logo.a"; sourceTree = "<group>"; };
+		907D02FF15B86F8700575110 /* libosgdb_lwo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_lwo.a; path = "../../../../osg-ios/lib/libosgdb_lwo.a"; sourceTree = "<group>"; };
+		907D030015B86F8700575110 /* libosgdb_lws.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_lws.a; path = "../../../../osg-ios/lib/libosgdb_lws.a"; sourceTree = "<group>"; };
+		907D030115B86F8700575110 /* libosgdb_md2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_md2.a; path = "../../../../osg-ios/lib/libosgdb_md2.a"; sourceTree = "<group>"; };
+		907D030215B86F8700575110 /* libosgdb_mdl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_mdl.a; path = "../../../../osg-ios/lib/libosgdb_mdl.a"; sourceTree = "<group>"; };
+		907D030315B86F8700575110 /* libosgdb_normals.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_normals.a; path = "../../../../osg-ios/lib/libosgdb_normals.a"; sourceTree = "<group>"; };
+		907D030415B86F8700575110 /* libosgdb_obj.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_obj.a; path = "../../../../osg-ios/lib/libosgdb_obj.a"; sourceTree = "<group>"; };
+		907D030515B86F8700575110 /* libosgdb_ogr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ogr.a; path = "../../../../osg-ios/lib/libosgdb_ogr.a"; sourceTree = "<group>"; };
+		907D030615B86F8700575110 /* libosgdb_openflight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_openflight.a; path = "../../../../osg-ios/lib/libosgdb_openflight.a"; sourceTree = "<group>"; };
+		907D030715B86F8700575110 /* libosgdb_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osg.a; path = "../../../../osg-ios/lib/libosgdb_osg.a"; sourceTree = "<group>"; };
+		907D030815B86F8700575110 /* libosgdb_osga.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osga.a; path = "../../../../osg-ios/lib/libosgdb_osga.a"; sourceTree = "<group>"; };
+		907D030915B86F8700575110 /* libosgdb_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgshadow.a; path = "../../../../osg-ios/lib/libosgdb_osgshadow.a"; sourceTree = "<group>"; };
+		907D030A15B86F8700575110 /* libosgdb_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgterrain.a; path = "../../../../osg-ios/lib/libosgdb_osgterrain.a"; sourceTree = "<group>"; };
+		907D030B15B86F8700575110 /* libosgdb_osgtgz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgtgz.a; path = "../../../../osg-ios/lib/libosgdb_osgtgz.a"; sourceTree = "<group>"; };
+		907D030C15B86F8700575110 /* libosgdb_osgviewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgviewer.a; path = "../../../../osg-ios/lib/libosgdb_osgviewer.a"; sourceTree = "<group>"; };
+		907D030D15B86F8700575110 /* libosgdb_p3d.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_p3d.a; path = "../../../../osg-ios/lib/libosgdb_p3d.a"; sourceTree = "<group>"; };
+		907D030E15B86F8700575110 /* libosgdb_pic.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pic.a; path = "../../../../osg-ios/lib/libosgdb_pic.a"; sourceTree = "<group>"; };
+		907D030F15B86F8700575110 /* libosgdb_ply.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ply.a; path = "../../../../osg-ios/lib/libosgdb_ply.a"; sourceTree = "<group>"; };
+		907D031015B86F8700575110 /* libosgdb_pnm.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pnm.a; path = "../../../../osg-ios/lib/libosgdb_pnm.a"; sourceTree = "<group>"; };
+		907D031115B86F8700575110 /* libosgdb_pov.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pov.a; path = "../../../../osg-ios/lib/libosgdb_pov.a"; sourceTree = "<group>"; };
+		907D031215B86F8700575110 /* libosgdb_pvr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pvr.a; path = "../../../../osg-ios/lib/libosgdb_pvr.a"; sourceTree = "<group>"; };
+		907D031315B86F8700575110 /* libosgdb_revisions.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_revisions.a; path = "../../../../osg-ios/lib/libosgdb_revisions.a"; sourceTree = "<group>"; };
+		907D031415B86F8700575110 /* libosgdb_rgb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_rgb.a; path = "../../../../osg-ios/lib/libosgdb_rgb.a"; sourceTree = "<group>"; };
+		907D031515B86F8700575110 /* libosgdb_rot.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_rot.a; path = "../../../../osg-ios/lib/libosgdb_rot.a"; sourceTree = "<group>"; };
+		907D031615B86F8700575110 /* libosgdb_scale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_scale.a; path = "../../../../osg-ios/lib/libosgdb_scale.a"; sourceTree = "<group>"; };
+		907D031715B86F8700575110 /* libosgdb_serializers_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osg.a; path = "../../../../osg-ios/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/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/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/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/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/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/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/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/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/lib/libosgdb_serializers_osgvolume.a"; sourceTree = "<group>"; };
+		907D032115B86F8700575110 /* libosgdb_shp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_shp.a; path = "../../../../osg-ios/lib/libosgdb_shp.a"; sourceTree = "<group>"; };
+		907D032215B86F8700575110 /* libosgdb_stl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_stl.a; path = "../../../../osg-ios/lib/libosgdb_stl.a"; sourceTree = "<group>"; };
+		907D032315B86F8700575110 /* libosgdb_tga.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tga.a; path = "../../../../osg-ios/lib/libosgdb_tga.a"; sourceTree = "<group>"; };
+		907D032415B86F8700575110 /* libosgdb_tgz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tgz.a; path = "../../../../osg-ios/lib/libosgdb_tgz.a"; sourceTree = "<group>"; };
+		907D032515B86F8700575110 /* libosgdb_trans.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_trans.a; path = "../../../../osg-ios/lib/libosgdb_trans.a"; sourceTree = "<group>"; };
+		907D032615B86F8700575110 /* libosgdb_txf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_txf.a; path = "../../../../osg-ios/lib/libosgdb_txf.a"; sourceTree = "<group>"; };
+		907D032715B86F8700575110 /* libosgdb_txp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_txp.a; path = "../../../../osg-ios/lib/libosgdb_txp.a"; sourceTree = "<group>"; };
+		907D032815B86F8700575110 /* libosgdb_vtf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_vtf.a; path = "../../../../osg-ios/lib/libosgdb_vtf.a"; sourceTree = "<group>"; };
+		907D032915B86F8700575110 /* libosgdb_x.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_x.a; path = "../../../../osg-ios/lib/libosgdb_x.a"; sourceTree = "<group>"; };
+		907D032A15B86F8700575110 /* libosgdb_zip.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_zip.a; path = "../../../../osg-ios/lib/libosgdb_zip.a"; sourceTree = "<group>"; };
+		907D032B15B86F8700575110 /* libosgDB.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgDB.a; path = "../../../../osg-ios/lib/libosgDB.a"; sourceTree = "<group>"; };
+		907D032C15B86F8700575110 /* libosgFX.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgFX.a; path = "../../../../osg-ios/lib/libosgFX.a"; sourceTree = "<group>"; };
+		907D032D15B86F8700575110 /* libosgGA.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgGA.a; path = "../../../../osg-ios/lib/libosgGA.a"; sourceTree = "<group>"; };
+		907D032E15B86F8700575110 /* libosgManipulator.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgManipulator.a; path = "../../../../osg-ios/lib/libosgManipulator.a"; sourceTree = "<group>"; };
+		907D032F15B86F8700575110 /* libosgParticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgParticle.a; path = "../../../../osg-ios/lib/libosgParticle.a"; sourceTree = "<group>"; };
+		907D033015B86F8700575110 /* libosgPresentation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgPresentation.a; path = "../../../../osg-ios/lib/libosgPresentation.a"; sourceTree = "<group>"; };
+		907D033115B86F8700575110 /* libosgShadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgShadow.a; path = "../../../../osg-ios/lib/libosgShadow.a"; sourceTree = "<group>"; };
+		907D033215B86F8700575110 /* libosgSim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgSim.a; path = "../../../../osg-ios/lib/libosgSim.a"; sourceTree = "<group>"; };
+		907D033315B86F8700575110 /* libosgTerrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgTerrain.a; path = "../../../../osg-ios/lib/libosgTerrain.a"; sourceTree = "<group>"; };
+		907D033415B86F8700575110 /* libosgText.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgText.a; path = "../../../../osg-ios/lib/libosgText.a"; sourceTree = "<group>"; };
+		907D033515B86F8700575110 /* libosgUtil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgUtil.a; path = "../../../../osg-ios/lib/libosgUtil.a"; sourceTree = "<group>"; };
+		907D033615B86F8700575110 /* libosgViewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgViewer.a; path = "../../../../osg-ios/lib/libosgViewer.a"; sourceTree = "<group>"; };
+		907D033715B86F8700575110 /* libosgVolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgVolume.a; path = "../../../../osg-ios/lib/libosgVolume.a"; sourceTree = "<group>"; };
+		907D033815B86F8700575110 /* libosgWidget.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgWidget.a; path = "../../../../osg-ios/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>"; };
@@ -350,6 +354,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				904E22771691CC42002D66FD /* Accelerate.framework in Frameworks */,
+				904E22781691CC42002D66FD /* MobileCoreServices.framework in Frameworks */,
 				907D037E15B86F8700575110 /* libosgdb_shp.a in Frameworks */,
 				9048FB2415FA9DE50012C900 /* libosgdb_tiff.a in Frameworks */,
 				907D03FA15B8C31A00575110 /* libsqlite3.dylib in Frameworks */,
@@ -566,6 +572,8 @@
 		9051FFFD15B1EDFD00D9ABD3 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				904E22751691CC42002D66FD /* Accelerate.framework */,
+				904E22761691CC42002D66FD /* MobileCoreServices.framework */,
 				905100CA15B2101E00D9ABD3 /* QuartzCore.framework */,
 				905100C415B20AD100D9ABD3 /* CoreImage.framework */,
 				905100C215B20AC600D9ABD3 /* ImageIO.framework */,
@@ -967,22 +975,23 @@
 				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",
+					../../,
+					"../../../../osg-ios/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\"",
+					"\"$(SRCROOT)/../../../../osg-ios/lib\"",
 				);
 				PRODUCT_NAME = osgEarth;
 				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "C5A8E349-91DB-4E98-9612-789DB499534E";
+				VALID_ARCHS = armv7;
 				WRAPPER_EXTENSION = app;
 			};
 			name = Debug;
@@ -997,22 +1006,23 @@
 				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",
+					../../,
+					"../../../../osg-ios/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\"",
+					"\"$(SRCROOT)/../../../../osg-ios/lib\"",
 				);
 				PRODUCT_NAME = osgEarth;
 				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "C5A8E349-91DB-4E98-9612-789DB499534E";
+				VALID_ARCHS = armv7;
 				WRAPPER_EXTENSION = app;
 			};
 			name = Release;
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
index a30011f..fceaa0c 100644
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
@@ -11,14 +11,9 @@
 
 #include <osgDB/FileUtils>
 
-#include <osgGA/MultiTouchTrackballManipulator>
-
 #include <osgViewer/api/IOS/GraphicsWindowIOS>
 
 #include <osgEarth/Viewpoint>
-#include <osgEarthUtil/EarthManipulator>
-#include <osgEarthUtil/AutoClipPlaneHandler>
-#include <osgEarthUtil/ObjectLocator>
 #include <osgEarthUtil/SkyNode>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
@@ -29,70 +24,6 @@
 using namespace osgEarth;
 using namespace osgEarth::Util;
 
-class ClampObjectLocatorCallback : public osgEarth::TerrainCallback
-{
-public:
-    ClampObjectLocatorCallback(ObjectLocatorNode* locator):
-    _locator(locator),
-    _maxLevel(-1),
-    _minLevel(0)
-    {
-    }
-    
-    virtual void onTileAdded(const osgEarth::TileKey& tileKey, osg::Node* terrain, TerrainCallbackContext&)
-    {           
-        if ((int)tileKey.getLevelOfDetail() > _minLevel && _maxLevel < (int)tileKey.getLevelOfDetail())
-        {
-            osg::Vec3d position = _locator->getLocator()->getPosition();
-            
-            if (tileKey.getExtent().contains(position.x(), position.y()))
-            {
-                //Compute our location in geocentric
-                const osg::EllipsoidModel* ellipsoid = tileKey.getProfile()->getSRS()->getEllipsoid();
-                double x, y, z;            
-                ellipsoid->convertLatLongHeightToXYZ(
-                                                     osg::DegreesToRadians(position.y()), osg::DegreesToRadians(position.x()), 0,
-                                                     x, y, z);
-                //Compute the up vector
-                osg::Vec3d up = ellipsoid->computeLocalUpVector(x, y, z );
-                up.normalize();
-                osg::Vec3d world(x, y, z);
-                
-                double segOffset = 50000;
-                
-                osg::Vec3d start = world + (up * segOffset);
-                osg::Vec3d end = world - (up * segOffset);
-                
-                osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
-                
-                osgUtil::IntersectionVisitor iv;            
-                iv.setIntersector( i );
-                terrain->accept( iv );
-                
-                osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
-                if ( !results.empty() )
-                {
-                    const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
-                    osg::Vec3d hit = result.getWorldIntersectPoint();
-                    double lat, lon, height;
-                    ellipsoid->convertXYZToLatLongHeight(hit.x(), hit.y(), hit.z(), 
-                                                         lat, lon, height);                
-                    position.z() = height;
-                    //OE_NOTICE << "Got hit, setting new height to " << height << std::endl;
-                    _maxLevel = tileKey.getLevelOfDetail();
-                    _locator->getLocator()->setPosition( position );
-                }            
-            }            
-        }            
-        
-    }
-    
-    osg::ref_ptr< ObjectLocatorNode > _locator;
-    int _maxLevel;
-    int _minLevel;
-};
-
-
 
 @interface ViewController () {
 
@@ -126,18 +57,7 @@ public:
 - (void)loadOsgEarthDemoScene{
 
     // install our default manipulator (do this before calling load)
-    _viewer->setCameraManipulator( new osgEarth::Util::EarthMultiTouchManipulator() );
-    
-    osg::Light* light = new osg::Light( 0 );  
-    light->setPosition( osg::Vec4(0, -1, 0, 0 ) );
-    light->setAmbient( osg::Vec4(0.4f, 0.4f, 0.4f ,1.0) );
-    light->setDiffuse( osg::Vec4(1,1,1,1) );
-    light->setSpecular( osg::Vec4(0,0,0,1) );
-
-    osg::Material* material = new osg::Material();
-    material->setAmbient(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
-    material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.9,0.9,0.9,1.0));
-    material->setSpecular(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
+    _viewer->setCameraManipulator( new osgEarth::Util::EarthManipulator() );
     
     
     osg::Node* node = osgDB::readNodeFile(osgDB::findDataFile("tests/" + _file));
@@ -164,64 +84,23 @@ public:
     // a root node to hold everything:
     osg::Group* root = new osg::Group();
     root->addChild( mapNode.get() );
-    //root->getOrCreateStateSet()->setAttribute(light);
+
     
     //have to add these
-    root->getOrCreateStateSet()->setAttribute(material);
-    //root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
+    osg::Material* material = new osg::Material();
+    material->setAmbient(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
+    material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.9,0.9,0.9,1.0));
+    material->setSpecular(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
+    root->getOrCreateStateSet()->setAttribute(material); //lighting doesn't work without a material for some reason
+    root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);//comment out to disable lighting
     
     double hours = 12.0f;
     float ambientBrightness = 0.4f;
     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 );
-    
-    
-    //for some reason we have to do this as global stateset doesn't
-    //appear to be in the statesetstack
-    root->getOrCreateStateSet()->setAttribute(_viewer->getLight());
-    
-    
-    //add model
-    /*unsigned int numObjects = 2;
-    osg::Group* treeGroup = new osg::Group();
-    root->addChild(treeGroup);
-    osg::Node* tree = osgDB::readNodeFile("./data/boxman.osg");         
-    osg::MatrixTransform* mt = new osg::MatrixTransform();
-    mt->setMatrix(osg::Matrixd::scale(1000,1000,1000));
-    mt->addChild( tree );
-    //Create bound around mt rainer
-    double centerLat =  46.840866;
-    double centerLon = -121.769846;
-    double height = 0.2;
-    double width = 0.2;
-    double minLat = centerLat - (height/2.0);
-    double minLon = centerLon - (width/2.0);
-    
-    OE_NOTICE << "Placing " << numObjects << " trees" << std::endl;
-    
-    for (unsigned int i = 0; i < numObjects; i++)
-    {
-        osgEarth::Util::ObjectLocatorNode* locator = new osgEarth::Util::ObjectLocatorNode( mapNode->getMap() );        
-        double lat = minLat + height * (rand() * 1.0)/(RAND_MAX-1);
-        double lon = minLon + width * (rand() * 1.0)/(RAND_MAX-1);        
-        //OE_NOTICE << "Placing tree at " << lat << ", " << lon << std::endl;
-        locator->getLocator()->setPosition(osg::Vec3d(lon,  lat, 0 ) );        
-        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);
-    
-    osgUtil::GLES2ShaderGenVisitor shaderGen;
-    //treeGroup->accept(shaderGen);
+    sky->attach( _viewer, 0 );
+    root->addChild( sky );
     
     _viewer->setSceneData( root );
 }
@@ -255,7 +134,7 @@ public:
 	traits->y = 0;
 	traits->width = w*scale;
 	traits->height = h*scale;
-	traits->depth = 24; //keep memory down, default is currently 24
+	traits->depth = 24;
 	traits->alpha = 8;
     //traits->samples = 4;
     //traits->sampleBuffers = 2;
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
index 5329526..eafb485 100755
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
+++ b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
@@ -63,8 +63,8 @@ USE_OSGPLUGIN(osgearth_feature_ogr)
 USE_OSGPLUGIN(osgearth_model_feature_stencil)
 USE_OSGPLUGIN(osgearth_vdatum_egm2008)
 USE_OSGPLUGIN(osgearth_model_simple)
-USE_OSGPLUGIN(osgearth_engine_osgterrain)
-//USE_OSGPLUGIN(osgearth_engine_quadtree)
+//USE_OSGPLUGIN(osgearth_engine_osgterrain)
+USE_OSGPLUGIN(osgearth_engine_quadtree)
 USE_OSGPLUGIN(osgearth_vdatum_egm96)
 USE_OSGPLUGIN(osgearth_ocean_surface)
 USE_OSGPLUGIN(osgearth_debug)
diff --git a/src/applications/osgearth_viewer_android/jni/OsgMainApp.cpp b/src/applications/osgearth_viewer_android/jni/OsgMainApp.cpp
new file mode 100644
index 0000000..29da676
--- /dev/null
+++ b/src/applications/osgearth_viewer_android/jni/OsgMainApp.cpp
@@ -0,0 +1,258 @@
+#include "OsgMainApp.hpp"
+
+#include <osgDB/FileUtils>
+#include <osgEarth/Capabilities>
+#include <osgEarthUtil/AutoClipPlaneHandler>
+#include <osgEarthUtil/ObjectLocator>
+#include <osgEarthDrivers/tms/TMSOptions>
+
+#include <osgDB/WriteFile>
+
+#include "GLES2ShaderGenVisitor.h"
+
+using namespace osgEarth;
+using namespace osgEarth::Drivers;
+using namespace osgEarth::Util;
+
+class ClampObjectLocatorCallback : public osgEarth::TerrainCallback
+{
+public:
+    ClampObjectLocatorCallback(ObjectLocatorNode* locator):
+    _locator(locator),
+    _maxLevel(-1),
+    _minLevel(0)
+    {
+    }
+    
+    virtual void onTileAdded(const osgEarth::TileKey& tileKey, osg::Node* terrain, TerrainCallbackContext&)
+    {
+        if ((int)tileKey.getLevelOfDetail() > _minLevel && _maxLevel < (int)tileKey.getLevelOfDetail())
+        {
+            osg::Vec3d position = _locator->getLocator()->getPosition();
+            
+            if (tileKey.getExtent().contains(position.x(), position.y()))
+            {
+                //Compute our location in geocentric
+                const osg::EllipsoidModel* ellipsoid = tileKey.getProfile()->getSRS()->getEllipsoid();
+                double x, y, z;
+                ellipsoid->convertLatLongHeightToXYZ(
+                                                     osg::DegreesToRadians(position.y()), osg::DegreesToRadians(position.x()), 0,
+                                                     x, y, z);
+                //Compute the up vector
+                osg::Vec3d up = ellipsoid->computeLocalUpVector(x, y, z );
+                up.normalize();
+                osg::Vec3d world(x, y, z);
+                
+                double segOffset = 50000;
+                
+                osg::Vec3d start = world + (up * segOffset);
+                osg::Vec3d end = world - (up * segOffset);
+                
+                osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
+                
+                osgUtil::IntersectionVisitor iv;
+                iv.setIntersector( i );
+                terrain->accept( iv );
+                
+                osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
+                if ( !results.empty() )
+                {
+                    const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
+                    osg::Vec3d hit = result.getWorldIntersectPoint();
+                    double lat, lon, height;
+                    ellipsoid->convertXYZToLatLongHeight(hit.x(), hit.y(), hit.z(),
+                                                         lat, lon, height);
+                    position.z() = height;
+                    //OE_NOTICE << "Got hit, setting new height to " << height << std::endl;
+                    _maxLevel = tileKey.getLevelOfDetail();
+                    _locator->getLocator()->setPosition( position );
+                }
+            }
+        }
+        
+    }
+    
+    osg::ref_ptr< ObjectLocatorNode > _locator;
+    int _maxLevel;
+    int _minLevel;
+};
+
+
+OsgMainApp::OsgMainApp(){
+
+    _initialized = false;
+
+}
+OsgMainApp::~OsgMainApp(){
+
+}
+
+
+//Initialization function
+void OsgMainApp::initOsgWindow(int x,int y,int width,int height){
+
+    __android_log_write(ANDROID_LOG_ERROR, "OSGANDROID",
+            "Initializing geometry");
+
+    //Pending
+    _notifyHandler = new OsgAndroidNotifyHandler();
+    _notifyHandler->setTag("Osg Viewer");
+    osg::setNotifyHandler(_notifyHandler);
+    osgEarth::setNotifyHandler(_notifyHandler);
+    
+    osg::setNotifyLevel(osg::FATAL);
+    osgEarth::setNotifyLevel(osg::INFO);
+
+    osg::notify(osg::ALWAYS)<<"Testing"<<std::endl;
+
+    //::setenv("OSGEARTH_HTTP_DEBUG", "1", 1);
+    //::setenv("OSGEARTH_DUMP_SHADERS", "1", 1);
+    
+    osgEarth::Registry::instance()->setDefaultTerrainEngineDriverName("quadtree");
+    
+    _bufferWidth = width;
+    _bufferHeight = height;
+    
+    _viewer = new osgViewer::Viewer();
+    _viewer->setUpViewerAsEmbeddedInWindow(x, y, width, height);
+    _viewer->getCamera()->setViewport(new osg::Viewport(0, 0, width, height));
+    _viewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_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)width/height,
+                                                           0.1f, 10000.0f);
+    // configure the near/far so we don't clip things that are up close
+    _viewer->getCamera()->setNearFarRatio(0.00002);
+    _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
+
+    _viewer->getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
+
+    // install our default manipulator (do this before calling load)
+    //_viewer->setCameraManipulator( new osgEarth::Util::EarthMultiTouchManipulator() );
+    _viewer->setCameraManipulator( new osgEarth::Util::EarthManipulator() ); //EarthMultiTouchManipulator() );
+    
+#if 0
+    osg::Light* light = new osg::Light( 0 );
+    light->setPosition( osg::Vec4(0, -1, 0, 0 ) );
+    light->setAmbient( osg::Vec4(0.4f, 0.4f, 0.4f ,1.0) );
+    light->setDiffuse( osg::Vec4(1,1,1,1) );
+    light->setSpecular( osg::Vec4(0,0,0,1) );
+    
+    osg::Material* material = new osg::Material();
+    material->setAmbient(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
+    material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.9,0.9,0.9,1.0));
+    material->setSpecular(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
+#endif
+    
+    osg::Node* node = osgDB::readNodeFile("http://readymap.org/readymap/maps/public/2.earth");
+    if ( !node )
+    {
+        OSG_ALWAYS << "Unable to load an earth file from the command line." << std::endl;
+        return;
+    }
+    
+    osg::ref_ptr<osgEarth::Util::MapNode> mapNode = osgEarth::Util::MapNode::findMapNode(node);
+    if ( !mapNode.valid() )
+    {
+        OSG_ALWAYS << "Loaded scene graph does not contain a MapNode - aborting" << std::endl;
+        return;
+    }
+    
+    // warn about not having an earth manip
+    osgEarth::Util::EarthManipulator* manip = dynamic_cast<osgEarth::Util::EarthManipulator*>(_viewer->getCameraManipulator());
+    if ( manip == 0L )
+    {
+        OSG_ALWAYS << "Helper used before installing an EarthManipulator" << std::endl;
+    }
+    
+    // a root node to hold everything:
+    osg::Group* root = new osg::Group();
+    root->addChild( mapNode.get() );
+    //root->getOrCreateStateSet()->setAttribute(light);
+    
+#if 0
+    //have to add these
+    root->getOrCreateStateSet()->setAttribute(material);
+    //root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
+#endif
+    double hours = 12.0f;
+    float ambientBrightness = 0.4f;
+    osgEarth::Util::SkyNode* sky = new osgEarth::Util::SkyNode( mapNode->getMap() );
+    sky->setAmbientBrightness( ambientBrightness );
+    sky->setDateTime( 1984, 11, 8, hours );
+    sky->attach( _viewer );
+    root->addChild( sky );
+
+
+    root->getOrCreateStateSet()->setMode(GL_LIGHTING,
+    		osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
+    
+    //for some reason we have to do this as global stateset doesn't
+    //appear to be in the statesetstack
+    root->getOrCreateStateSet()->setAttribute(_viewer->getLight());
+    
+    _viewer->setSceneData( root );
+
+    _viewer->setRunFrameScheme( osgViewer::ViewerBase::ON_DEMAND );
+
+    _viewer->realize();
+
+    _initialized = true;
+
+}
+//Draw
+void OsgMainApp::draw(){
+
+    _viewer->frame();
+    
+    //clear events for next frame
+    _frameTouchBeganEvents = NULL;
+    _frameTouchMovedEvents = NULL;
+    _frameTouchEndedEvents = NULL;
+}
+//Events
+static bool flipy = true;
+void OsgMainApp::touchBeganEvent(int touchid,float x,float y){
+    if (!_frameTouchBeganEvents.valid()) {
+        if(_viewer.valid()){
+            _frameTouchBeganEvents = _viewer->getEventQueue()->touchBegan(touchid, osgGA::GUIEventAdapter::TOUCH_BEGAN, x, flipy ? _bufferHeight-y : y);
+        }
+    } else {
+        _frameTouchBeganEvents->addTouchPoint(touchid, osgGA::GUIEventAdapter::TOUCH_BEGAN, x, flipy ? _bufferHeight-y : y);
+    }
+}
+void OsgMainApp::touchMovedEvent(int touchid,float x,float y){
+    if (!_frameTouchMovedEvents.valid()) {
+        if(_viewer.valid()){
+            _frameTouchMovedEvents = _viewer->getEventQueue()->touchMoved(touchid, osgGA::GUIEventAdapter::TOUCH_MOVED, x, flipy ? _bufferHeight-y : y);
+        }
+    } else {
+        _frameTouchMovedEvents->addTouchPoint(touchid, osgGA::GUIEventAdapter::TOUCH_MOVED, x, flipy ? _bufferHeight-y : y);
+    }
+}
+void OsgMainApp::touchEndedEvent(int touchid,float x,float y,int tapcount){
+    if (!_frameTouchEndedEvents.valid()) {
+        if(_viewer.valid()){
+            _frameTouchEndedEvents = _viewer->getEventQueue()->touchEnded(touchid, osgGA::GUIEventAdapter::TOUCH_ENDED, x, flipy ? _bufferHeight-y : y,tapcount);
+        }
+    } else {
+        _frameTouchEndedEvents->addTouchPoint(touchid, osgGA::GUIEventAdapter::TOUCH_ENDED, x, flipy ? _bufferHeight-y : y,tapcount);
+    }
+}
+void OsgMainApp::keyboardDown(int key){
+    _viewer->getEventQueue()->keyPress(key);
+}
+void OsgMainApp::keyboardUp(int key){
+    _viewer->getEventQueue()->keyRelease(key);
+}
+
+void OsgMainApp::clearEventQueue()
+{
+    //clear our groups
+    _frameTouchBeganEvents = NULL;
+    _frameTouchMovedEvents = NULL;
+    _frameTouchEndedEvents = NULL;
+    
+    //clear the viewers queue
+    _viewer->getEventQueue()->clear();
+}
diff --git a/src/osgEarth/Bounds b/src/osgEarth/Bounds
index 41fa5a7..160863b 100644
--- a/src/osgEarth/Bounds
+++ b/src/osgEarth/Bounds
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Bounds.cpp b/src/osgEarth/Bounds.cpp
index 98b4ae4..7beea5f 100644
--- a/src/osgEarth/Bounds.cpp
+++ b/src/osgEarth/Bounds.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CMakeLists.txt b/src/osgEarth/CMakeLists.txt
index 2175736..3617471 100644
--- a/src/osgEarth/CMakeLists.txt
+++ b/src/osgEarth/CMakeLists.txt
@@ -27,6 +27,8 @@ SET(LIB_PUBLIC_HEADERS
     CachePolicy
     CacheSeed
     Capabilities
+    ClampableNode
+    ClampingTechnique
     ColorFilter
     Common
     CompositeTileSource
@@ -38,6 +40,7 @@ SET(LIB_PUBLIC_HEADERS
     DPLineSegmentIntersector
     Draggers
     DrapeableNode
+    DrapingTechnique
     DrawInstanced
     ECEF
     ElevationLayer
@@ -81,6 +84,7 @@ SET(LIB_PUBLIC_HEADERS
     Notify
     optional
     OverlayDecorator
+    OverlayNode
     Pickers
     Profile
     Progress
@@ -104,6 +108,8 @@ SET(LIB_PUBLIC_HEADERS
     TextureCompositorTexArray
     TileKey
     TileSource
+    TimeControl
+    TraversalData
     ThreadingUtils
     Units
     URI
@@ -141,6 +147,8 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     CachePolicy.cpp
     CacheSeed.cpp
     Capabilities.cpp
+    ClampableNode.cpp
+    ClampingTechnique.cpp
     ColorFilter.cpp
     CompositeTileSource.cpp
     Config.cpp
@@ -150,6 +158,7 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     Draggers.cpp
     DPLineSegmentIntersector.cpp
     DrapeableNode.cpp
+    DrapingTechnique.cpp
     DrawInstanced.cpp
     ECEF.cpp
     ElevationLayer.cpp
@@ -188,6 +197,7 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     NodeUtils.cpp
     Notify.cpp
     OverlayDecorator.cpp
+    OverlayNode.cpp
     Pickers.cpp
     Profile.cpp
     Progress.cpp
@@ -211,6 +221,8 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     TextureCompositorTexArray.cpp
     TileKey.cpp
     TileSource.cpp
+    TimeControl.cpp
+    TraversalData.cpp
     ThreadingUtils.cpp
     Units.cpp
     URI.cpp
@@ -234,13 +246,13 @@ ELSE(WIN32)
   LINK_EXTERNAL(${LIB_NAME} ${TARGET_EXTERNAL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${MATH_LIBRARY} )
 ENDIF(WIN32)
 
-#LINK_WITH_VARIABLES(${LIB_NAME} OSG_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGDB_LIBRARY OSGFX_LIBRARY OSGVIEWER_LIBRARY OSGTEXT_LIBRARY OSGGA_LIBRARY OSGSHADOW_LIBRARY OPENTHREADS_LIBRARY CURL_LIBRARY GDAL_LIBRARY ZLIB_LIBRARY)
+#LINK_WITH_VARIABLES(${LIB_NAME} OSG_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGDB_LIBRARY OSGFX_LIBRARY OSGVIEWER_LIBRARY OSGTEXT_LIBRARY OSGGA_LIBRARY OSGSHADOW_LIBRARY OPENTHREADS_LIBRARY CURL_LIBRARY GDAL_LIBRARY ZLIB_LIBRARY OSGMANIPULATOR_LIBRARY)
 
 OPTION(NRL_STATIC_LIBRARIES "Link osgEarth against static GDAL and cURL, including static OpenSSL, Proj4, JPEG, PNG, and TIFF." OFF)
 if(NOT NRL_STATIC_LIBRARIES)
-  LINK_WITH_VARIABLES(${LIB_NAME} OSG_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGDB_LIBRARY OSGFX_LIBRARY OSGVIEWER_LIBRARY OSGTEXT_LIBRARY OSGGA_LIBRARY OSGSHADOW_LIBRARY OPENTHREADS_LIBRARY CURL_LIBRARY GDAL_LIBRARY ZLIB_LIBRARY)
+  LINK_WITH_VARIABLES(${LIB_NAME} OSG_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGDB_LIBRARY OSGFX_LIBRARY OSGVIEWER_LIBRARY OSGTEXT_LIBRARY OSGGA_LIBRARY OSGSHADOW_LIBRARY OPENTHREADS_LIBRARY CURL_LIBRARY GDAL_LIBRARY ZLIB_LIBRARY OSGMANIPULATOR_LIBRARY)
 else(NOT NRL_STATIC_LIBRARIES)
-  LINK_WITH_VARIABLES(${LIB_NAME} OSG_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGDB_LIBRARY OSGFX_LIBRARY OSGVIEWER_LIBRARY OSGTEXT_LIBRARY OSGGA_LIBRARY OSGSHADOW_LIBRARY OPENTHREADS_LIBRARY CURL_LIBRARY GDAL_LIBRARY ZLIB_LIBRARY SSL_EAY_RELEASE LIB_EAY_RELEASE TIFF_LIBRARY PROJ4_LIBRARY PNG_LIBRARY JPEG_LIBRARY)
+  LINK_WITH_VARIABLES(${LIB_NAME} OSG_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGDB_LIBRARY OSGFX_LIBRARY OSGVIEWER_LIBRARY OSGTEXT_LIBRARY OSGGA_LIBRARY OSGSHADOW_LIBRARY OPENTHREADS_LIBRARY CURL_LIBRARY GDAL_LIBRARY ZLIB_LIBRARY OSGMANIPULATOR_LIBRARY SSL_EAY_RELEASE LIB_EAY_RELEASE TIFF_LIBRARY PROJ4_LIBRARY PNG_LIBRARY JPEG_LIBRARY)
 endif(NOT NRL_STATIC_LIBRARIES)
 
 LINK_CORELIB_DEFAULT(${LIB_NAME} ${CMAKE_THREAD_LIBS_INIT} ${MATH_LIBRARY})
diff --git a/src/osgEarth/Cache b/src/osgEarth/Cache
index b5a79b2..8939041 100644
--- a/src/osgEarth/Cache
+++ b/src/osgEarth/Cache
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Cache.cpp b/src/osgEarth/Cache.cpp
index d96a8bf..907c175 100644
--- a/src/osgEarth/Cache.cpp
+++ b/src/osgEarth/Cache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CacheBin b/src/osgEarth/CacheBin
index 0dee96d..f9fbab2 100644
--- a/src/osgEarth/CacheBin
+++ b/src/osgEarth/CacheBin
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CachePolicy b/src/osgEarth/CachePolicy
index fa86de0..8221b86 100644
--- a/src/osgEarth/CachePolicy
+++ b/src/osgEarth/CachePolicy
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CachePolicy.cpp b/src/osgEarth/CachePolicy.cpp
index ea24bea..f91e1b3 100644
--- a/src/osgEarth/CachePolicy.cpp
+++ b/src/osgEarth/CachePolicy.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -109,6 +109,7 @@ CachePolicy::fromConfig( const Config& conf )
     conf.getIfSet( "usage", "read_only",  _usage, USAGE_READ_ONLY );
     conf.getIfSet( "usage", "cache_only", _usage, USAGE_CACHE_ONLY );
     conf.getIfSet( "usage", "no_cache",   _usage, USAGE_NO_CACHE );
+    conf.getIfSet( "usage", "none",       _usage, USAGE_NO_CACHE );
     conf.getIfSet( "max_age", _maxAge );
 }
 
diff --git a/src/osgEarth/CacheSeed b/src/osgEarth/CacheSeed
index 99d978a..0937cc4 100644
--- a/src/osgEarth/CacheSeed
+++ b/src/osgEarth/CacheSeed
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CacheSeed.cpp b/src/osgEarth/CacheSeed.cpp
index 4cfeb9b..035bd3b 100644
--- a/src/osgEarth/CacheSeed.cpp
+++ b/src/osgEarth/CacheSeed.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Capabilities b/src/osgEarth/Capabilities
index 5e56327..b9ebd20 100644
--- a/src/osgEarth/Capabilities
+++ b/src/osgEarth/Capabilities
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -105,6 +105,8 @@ namespace osgEarth
         /** maximum size of a uniform buffer block, in bytes */
         int getMaxUniformBlockSize() const { return _maxUniformBlockSize; }
 
+        /** whether to prefer display lists over VBOs for static geometry. */
+        bool preferDisplayListsForStaticGeometry() const { return _preferDLforStaticGeom; }
 
     protected:
         Capabilities();
@@ -134,6 +136,7 @@ namespace osgEarth
         bool _supportsDrawInstanced;
         bool _supportsUniformBufferObjects;
         int  _maxUniformBlockSize;
+        bool _preferDLforStaticGeom;
         std::string _vendor;
         std::string _renderer;
         std::string _version;
diff --git a/src/osgEarth/Capabilities.cpp b/src/osgEarth/Capabilities.cpp
index 8e331fb..8c89dc3 100644
--- a/src/osgEarth/Capabilities.cpp
+++ b/src/osgEarth/Capabilities.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -115,7 +115,8 @@ _supportsDepthPackedStencilBuffer( false ),
 _supportsOcclusionQuery ( false ),
 _supportsDrawInstanced  ( false ),
 _supportsUniformBufferObjects( false ),
-_maxUniformBlockSize    ( 0 )
+_maxUniformBlockSize    ( 0 ),
+_preferDLforStaticGeom  ( true )
 {
     // little hack to force the osgViewer library to link so we can create a graphics context
     osgViewerGetVersion();
@@ -228,15 +229,19 @@ _maxUniformBlockSize    ( 0 )
         OE_INFO << LC << "  depth-packed stencil = " << SAYBOOL(_supportsDepthPackedStencilBuffer) << std::endl;
 
         _supportsOcclusionQuery = osg::isGLExtensionSupported( id, "GL_ARB_occlusion_query" );
-        OE_INFO << LC << "  occulsion query = " << SAYBOOL(_supportsOcclusionQuery) << std::endl;
+        OE_INFO << LC << "  occlusion query = " << SAYBOOL(_supportsOcclusionQuery) << std::endl;
 
-        _supportsDrawInstanced = osg::isGLExtensionOrVersionSupported( id, "GL_EXT_draw_instanced", 3.1f );
+        _supportsDrawInstanced = 
+            _supportsGLSL &&
+            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 );
+        _supportsUniformBufferObjects = 
+            _supportsGLSL &&
+            osg::isGLExtensionOrVersionSupported( id, "GL_ARB_uniform_buffer_object", 2.0f );
         OE_INFO << LC << "  uniform buffer objects = " << SAYBOOL(_supportsUniformBufferObjects) << std::endl;
 
         if ( _supportsUniformBufferObjects && _maxUniformBlockSize == 0 )
@@ -249,6 +254,33 @@ _maxUniformBlockSize    ( 0 )
         //_supportsTexture2DLod = osg::isGLExtensionSupported( id, "GL_ARB_shader_texture_lod" );
         //OE_INFO << LC << "  texture2DLod = " << SAYBOOL(_supportsTexture2DLod) << std::endl;
 
+        // NVIDIA:
+        bool isNVIDIA = _vendor.find("NVIDIA") == 0;
+
+        // NVIDIA has h/w acceleration of some kind for display lists, supposedly.
+        // In any case they do benchmark much faster in osgEarth for static geom.
+        // BUT unfortunately, they dont' seem to work too well with shaders. Colors
+        // change randomly, etc. Might work OK for textured geometry but not for 
+        // untextured. TODO: investigate.
+#if 1
+        _preferDLforStaticGeom = false;
+        if ( ::getenv("OSGEARTH_TRY_DISPLAY_LISTS") )
+        {
+            _preferDLforStaticGeom = true;
+        }
+#else
+        if ( ::getenv("OSGEARTH_ALWAYS_USE_VBOS") )
+        {
+            _preferDLforStaticGeom = false;
+        }
+        else
+        {
+            _preferDLforStaticGeom = isNVIDIA;
+        }
+#endif
+
+        OE_INFO << LC << "  prefer DL for static geom = " << SAYBOOL(_preferDLforStaticGeom) << std::endl;
+
         // ATI workarounds:
         bool isATI = _vendor.find("ATI ") == 0;
 
diff --git a/src/osgEarth/ClampableNode b/src/osgEarth/ClampableNode
new file mode 100644
index 0000000..1e90f35
--- /dev/null
+++ b/src/osgEarth/ClampableNode
@@ -0,0 +1,85 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2011 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_CLAMPABLE_NODE_H
+#define OSGEARTH_CLAMPABLE_NODE_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/DepthOffset>
+#include <osgEarth/OverlayNode>
+#include <osg/Group>
+
+namespace osgEarth
+{
+    class MapNode;
+
+    /**
+     * Node graph that can be "clamped" on the MapNode terrain
+     * using the overlay decorator.
+     *
+     * Usage: Create this node and put it anywhere in the scene graph. The
+     * subgraph of this node will be draped on the MapNode's terrain.
+     */
+    class OSGEARTH_EXPORT ClampableNode : public OverlayNode
+    {
+    public:
+        /**
+         * Constructs a new clampable node.
+         */
+        ClampableNode( MapNode* mapNode, bool clamped =true );
+
+    public:
+        /** Depth Offsetting properties. Clamped geometry is automatically
+         *  subjected to depth offsetting to mitigate z-buffer conflicts */
+        DepthOffsetOptions& depthOffset() { dirtyDepthOffsetOptions(); return _do; }
+        const DepthOffsetOptions& depthOffset() const { return _do; }
+        
+        /** Automatically attempt to select a minimum bias based on an analysis
+         *  of the subgraph's geometry. */
+        void setAutoCalculateDepthOffset();
+
+        /** Backwards compatibility */
+        void setClamped( bool value ) { setActive(value); }
+        bool getClamped() const { return getActive(); }
+
+    public: // osg::Node
+
+        virtual osg::BoundingSphere computeBound() const;
+
+        virtual void traverse(osg::NodeVisitor& nv);
+
+    protected:
+        /** dtor */
+        virtual ~ClampableNode() { }
+
+        bool _autoBias;
+        mutable osg::ref_ptr<osg::Uniform> _biasUniform;
+        mutable osg::ref_ptr<osg::Uniform> _rangeUniform;
+
+        mutable DepthOffsetOptions _do;
+        bool _doDirty;
+
+        void init();
+        void dirtyDepthOffsetOptions();
+        void applyDepthOffsetOptions();
+    };
+
+} // namespace osgEarth
+
+#endif // OSGEARTH_CLAMPABLE_NODE_H
diff --git a/src/osgEarth/ClampableNode.cpp b/src/osgEarth/ClampableNode.cpp
new file mode 100644
index 0000000..eb208d1
--- /dev/null
+++ b/src/osgEarth/ClampableNode.cpp
@@ -0,0 +1,123 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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/ClampableNode>
+#include <osgEarth/ClampingTechnique>
+#include <osgEarth/DepthOffset>
+#include <osgEarth/OverlayDecorator>
+#include <osgEarth/MapNode>
+
+#define LC "[ClampableNode] "
+
+using namespace osgEarth;
+
+//------------------------------------------------------------------------
+
+namespace
+{
+    static osg::Group* getTechniqueGroup(MapNode* m)
+    {
+        return m ? m->getOverlayDecorator()->getGroup<ClampingTechnique>() : 0L;
+    }
+}
+
+//------------------------------------------------------------------------
+
+ClampableNode::ClampableNode( MapNode* mapNode, bool active ) :
+OverlayNode( mapNode, active, &getTechniqueGroup )
+{
+    init();
+}
+
+void
+ClampableNode::init()
+{
+    // auto-bias starts out true, but if you set the depth offset options
+    // it will toggle to false.
+    _autoBias = true;
+
+    _doDirty  = false;
+
+    osg::StateSet* s = this->getOrCreateStateSet();
+
+    _biasUniform = s->getOrCreateUniform( "oe_clamp_bias", osg::Uniform::FLOAT_VEC2 );
+    _biasUniform->set( osg::Vec2f(*_do.minBias(), *_do.maxBias()) );
+
+    _rangeUniform = s->getOrCreateUniform( "oe_clamp_range", osg::Uniform::FLOAT_VEC2 );
+    _rangeUniform->set( osg::Vec2f(*_do.minRange(), *_do.maxRange()) );
+}
+
+void
+ClampableNode::dirtyDepthOffsetOptions()
+{
+    if ( !_doDirty )
+    {
+        _doDirty = true;
+        _autoBias = false;
+        ADJUST_UPDATE_TRAV_COUNT( this, 1 );
+    }
+}
+
+void
+ClampableNode::traverse(osg::NodeVisitor& nv)
+{
+    if ( nv.getVisitorType() == nv.UPDATE_VISITOR )
+    {
+        applyDepthOffsetOptions();
+        _doDirty = false;
+        ADJUST_UPDATE_TRAV_COUNT( this, -1 );
+    }
+    OverlayNode::traverse(nv);
+}
+
+void
+ClampableNode::applyDepthOffsetOptions()
+{
+    if ( _do.enabled() == true )
+    {
+        _biasUniform->set( osg::Vec2f(*_do.minBias(), *_do.maxBias()) );
+        _rangeUniform->set( osg::Vec2f(*_do.minRange(), *_do.maxRange()) );
+        dirtyBound();
+    }
+    else
+    {
+        _biasUniform->set( osg::Vec2f(0.0f, 0.0f) );
+    }
+}
+
+void
+ClampableNode::setAutoCalculateDepthOffset()
+{
+    // prompts OSG to call computeBound() on the next pass which
+    // will recalculate the minimum bias.
+    _autoBias = true;
+    dirtyBound();
+}
+
+osg::BoundingSphere
+ClampableNode::computeBound() const
+{
+    if ( _autoBias && _do.enabled() == true )
+    {
+        _do.minBias() = DepthOffsetUtils::recalculate( this );
+        _biasUniform->set( osg::Vec2f(*_do.minBias(), *_do.maxBias()) );
+    }
+
+    return OverlayNode::computeBound();
+}
diff --git a/src/osgEarth/ClampingBinTechnique b/src/osgEarth/ClampingBinTechnique
new file mode 100644
index 0000000..b95e670
--- /dev/null
+++ b/src/osgEarth/ClampingBinTechnique
@@ -0,0 +1,105 @@
+/* -*-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.Ov
+*
+* 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_OVERLAY_CLAMPING_BIN_TECHNIQUE
+#define OSGEARTH_OVERLAY_CLAMPING_BIN_TECHNIQUE
+
+#include <osgEarth/Common>
+#include <osgEarth/OverlayDecorator>
+#include <osg/TexGenNode>
+#include <osg/Uniform>
+
+#define OSGEARTH_CLAMPING_BIN "osgEarth::ClampingBin"
+
+namespace osgEarth
+{
+    class TerrainEngineNode;
+
+    /**
+     * Clamps geometry to the terrain using the GPU.
+     *
+     * Uses the same technique for GPU clamping as the ClampingTechnique.
+     * See that class's header for details.
+     *
+     * The difference is that this technique uses a custom RenderBin rather
+     * than requiring the geometry be under a special overlay group; so,
+     * rather than use a ClampableNode, you can simply say
+     *
+     *   stateSet->setRenderBinDetail(0, OSGEARTH_CLAMPING_BIN);
+     *
+     * Limitations
+     *
+     * This technique is not active yet. The issue is that the RTT camera
+     * generator in OverlayDecorator needs to know the bounds of the overlay
+     * geometry in order to make a minimum bounding polyhedron. This technique
+     * does not provide that information (yet).
+     */
+    class OSGEARTH_EXPORT ClampingBinTechnique : public OverlayTechnique
+    {
+    public:
+        ClampingBinTechnique();
+
+        /**
+         * The size (resolution in both directions) of the depth map texture. By
+         * default, this defaults to 4096 or your hardware's maximum supported
+         * texture size, whichever is less.
+         */
+        void setTextureSize( int texSize );
+        int getTextureSize() const { return *_textureSize; }
+
+
+    public: // OverlayTechnique
+
+        virtual bool hasData(
+            OverlayDecorator::TechRTTParams& params) const;
+
+        void reestablish(
+            TerrainEngineNode* engine );
+
+        void preCullTerrain(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv );
+
+        void cullOverlayGroup(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv );
+
+        void onInstall( TerrainEngineNode* engine );
+
+        void onUninstall( TerrainEngineNode* engine );
+
+
+    protected:
+        virtual ~ClampingBinTechnique() { }
+
+    private:
+        int                _textureUnit;
+        optional<int>      _textureSize;
+        TerrainEngineNode* _engine;
+        Threading::PerObjectMap<osg::Camera*, osg::ref_ptr<osg::StateSet> > _perCameraStateSet;
+
+    public:
+        osg::StateSet* getStateSet(osg::Camera*);
+
+    private:
+        void setUpCamera(OverlayDecorator::TechRTTParams& params);
+    };
+
+} // namespace osgEarth
+
+#endif //OSGEARTH_OVERLAY_CLAMPING_BIN_TECHNIQUE
diff --git a/src/osgEarth/ClampingBinTechnique.cpp b/src/osgEarth/ClampingBinTechnique.cpp
new file mode 100644
index 0000000..398e929
--- /dev/null
+++ b/src/osgEarth/ClampingBinTechnique.cpp
@@ -0,0 +1,463 @@
+/* -*-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/ClampingBinTechnique>
+#include <osgEarth/Capabilities>
+#include <osgEarth/Registry>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/Utils>
+
+#include <osg/Depth>
+#include <osg/PolygonMode>
+#include <osg/Texture2D>
+#include <osg/Uniform>
+
+#define LC "[ClampingBinTechnique] "
+
+//#define OE_TEST if (_dumpRequested) OE_INFO << std::setprecision(9)
+#define OE_TEST OE_NULL
+
+using namespace osgEarth;
+
+//---------------------------------------------------------------------------
+
+
+
+namespace
+{
+    // Projection clamping callback that simply records the clamped matrix for
+    // later use
+    struct CPM : public osg::CullSettings::ClampProjectionMatrixCallback
+    {
+        void setup( osgUtil::CullVisitor* cv ) {
+            _clampedProj.makeIdentity();
+            _cv = cv;
+        }
+        bool clampProjectionMatrixImplementation(osg::Matrixf& projection, double& znear, double& zfar) const {
+            return _cv->clampProjectionMatrixImplementation(projection, znear, zfar);
+            OE_WARN << "NO!" << std::endl;
+        }
+        bool clampProjectionMatrixImplementation(osg::Matrixd& projection, double& znear, double& zfar) const {
+            bool r = _cv->clampProjectionMatrixImplementation(projection, znear, zfar);
+            if ( r ) _clampedProj = projection;
+            return r;
+        }
+        
+        osgUtil::CullVisitor* _cv;
+        mutable osg::Matrixd  _clampedProj;
+    };
+
+
+    // Additional per-view data stored by the clamping technique.
+    struct LocalPerViewData : public osg::Referenced
+    {
+        osg::ref_ptr<osg::Texture2D> _rttTexture;
+        osg::ref_ptr<osg::StateSet>  _groupStateSet;
+        osg::ref_ptr<osg::Uniform>   _camViewToDepthClipUniform;
+        osg::ref_ptr<osg::Uniform>   _depthClipToCamViewUniform;
+        osg::ref_ptr<CPM>            _cpm;
+        unsigned                     _renderLeafCount;
+    };
+}
+
+
+// Custom bin for clamping.
+namespace
+{
+    struct ClampingRenderBin : public osgUtil::RenderBin
+    {
+        struct PerViewData // : public osg::Referenced
+        {
+            osg::observer_ptr<LocalPerViewData> _techData;
+        };
+
+        // shared across ALL render bin instances.
+        typedef Threading::PerObjectMap<osg::Camera*, PerViewData> PerViewDataMap;
+        PerViewDataMap* _pvd; 
+
+        // support cloning (from RenderBin):
+        virtual osg::Object* cloneType() const { return new ClampingRenderBin(); }
+        virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new ClampingRenderBin(*this,copyop); } // note only implements a clone of type.
+        virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const ClampingRenderBin*>(obj)!=0L; }
+        virtual const char* libraryName() const { return "osgEarth"; }
+        virtual const char* className() const { return "ClampingRenderBin"; }
+
+
+        // constructs the prototype for this render bin.
+        ClampingRenderBin() : osgUtil::RenderBin()
+        {
+            this->setName( OSGEARTH_CLAMPING_BIN );
+            _pvd = new PerViewDataMap();
+        }
+
+        ClampingRenderBin( const ClampingRenderBin& rhs, const osg::CopyOp& op )
+            : osgUtil::RenderBin( rhs, op ), _pvd( rhs._pvd )
+        {
+            // zero out the stateset...dont' want to share that!
+            _stateset = 0L;
+        }
+
+        // override.
+        void sortImplementation()
+        {
+            copyLeavesFromStateGraphListToRenderLeafList();
+        }
+
+        // override.
+        void drawImplementation(osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
+        {
+            // find and initialize the state set for this camera.
+            if ( !_stateset.valid() )
+            {
+                osg::Camera* camera = renderInfo.getCurrentCamera();
+                PerViewData& data = _pvd->get(camera);
+                if ( data._techData.valid() )
+                {
+                    _stateset = data._techData->_groupStateSet.get();
+                    LocalPerViewData* local = static_cast<LocalPerViewData*>(data._techData.get());
+                    local->_renderLeafCount = _renderLeafList.size();
+                }
+            }
+
+            osgUtil::RenderBin::drawImplementation( renderInfo, previous );
+        }
+    };
+}
+
+/** the static registration. */
+extern "C" void osgEarth_clamping_bin_registration(void) {}
+static osgEarthRegisterRenderBinProxy<ClampingRenderBin> s_regbin(OSGEARTH_CLAMPING_BIN);
+
+
+//---------------------------------------------------------------------------
+
+ClampingBinTechnique::ClampingBinTechnique() :
+_textureSize     ( 4096 )
+{
+    // use the maximum available unit.
+    _textureUnit = Registry::capabilities().getMaxGPUTextureUnits() - 1;
+}
+
+
+bool 
+ClampingBinTechnique::hasData(OverlayDecorator::TechRTTParams& params) const
+{
+    LocalPerViewData* local = static_cast<LocalPerViewData*>(params._techniqueData.get());
+    return local && local->_renderLeafCount > 0;
+}
+
+
+void
+ClampingBinTechnique::reestablish(TerrainEngineNode* engine)
+{
+    // nop.
+}
+
+
+void
+ClampingBinTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
+{
+    // To store technique-specific per-view info:
+    LocalPerViewData* local = new LocalPerViewData();
+    params._techniqueData = local;
+
+    // set up a callback to extract the overlay projection matrix.
+    local->_cpm = new CPM();
+
+    // create the projected texture:
+    local->_rttTexture = new osg::Texture2D();
+    local->_rttTexture->setTextureSize( *_textureSize, *_textureSize );
+    local->_rttTexture->setInternalFormat( GL_DEPTH_COMPONENT );
+    local->_rttTexture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR );
+    local->_rttTexture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
+
+    // this is important. geometry that is outside the depth texture will clamp to the
+    // closest edge value in the texture -- this is good when you are rendering a 
+    // primitive that has one or more of its verts off-screen.
+    local->_rttTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE );
+    local->_rttTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
+    //local->_rttTexture->setBorderColor( osg::Vec4(0,0,0,1) );
+
+    // set up the RTT camera:
+    params._rttCamera = new osg::Camera();
+    params._rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
+    params._rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
+    params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+    params._rttCamera->setComputeNearFarMode( osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES );
+    params._rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize );
+    params._rttCamera->setRenderOrder( osg::Camera::PRE_RENDER );
+    params._rttCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
+    params._rttCamera->attach( osg::Camera::DEPTH_BUFFER, local->_rttTexture.get() );
+    params._rttCamera->setClampProjectionMatrixCallback( local->_cpm.get() );
+
+    // set up a StateSet for the RTT camera.
+    osg::StateSet* rttStateSet = params._rttCamera->getOrCreateStateSet();
+
+    // lighting is off. We don't want draped items to be lit.
+    //rttStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
+
+    rttStateSet->setMode(
+        GL_BLEND, 
+        osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
+
+    // prevents wireframe mode in the depth camera.
+    rttStateSet->setAttributeAndModes(
+        new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL ),
+        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+
+#if 0 //OOPS this kills things like a vertical scale shader!!
+    // installs a dirt-simple program for rendering the depth texture that
+    // skips all the normal terrain rendering stuff
+    osg::Program* depthProg = new osg::Program();
+    depthProg->addShader(new osg::Shader(
+        osg::Shader::VERTEX, 
+        "void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; }\n"));
+    depthProg->addShader(new osg::Shader(
+        osg::Shader::FRAGMENT, 
+        "void main() { gl_FragColor = vec4(1,1,1,1); }\n"));
+    rttStateSet->setAttributeAndModes(
+        depthProg,
+        osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED );
+#endif
+    
+    // attach the terrain to the camera.
+    // todo: should probably protect this with a mutex.....
+    params._rttCamera->addChild( _engine ); //params._terrainParent->getChild(0) ); // the terrain itself.
+
+    // assemble the overlay graph stateset.
+    local->_groupStateSet = new osg::StateSet();
+
+    local->_groupStateSet->setTextureAttributeAndModes( 
+        _textureUnit, 
+        local->_rttTexture.get(), 
+        osg::StateAttribute::ON );
+
+    // set up depth test/write parameters for the overlay geometry:
+    local->_groupStateSet->setAttributeAndModes(
+        new osg::Depth( osg::Depth::LEQUAL, 0.0, 1.0, false ),
+        osg::StateAttribute::ON );
+
+    local->_groupStateSet->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
+
+    // sampler for depth map texture:
+    local->_groupStateSet->getOrCreateUniform(
+        "oe_clamp_depthtex", 
+        osg::Uniform::SAMPLER_2D )->set( _textureUnit );
+
+    // matrix that transforms a vert from EYE coords to the depth camera's CLIP coord.
+    local->_camViewToDepthClipUniform = local->_groupStateSet->getOrCreateUniform( 
+        "oe_clamp_eye2depthclipmat", 
+        osg::Uniform::FLOAT_MAT4 );
+
+    // matrix that transforms a vert from depth-cam CLIP coords to EYE coords.
+    local->_depthClipToCamViewUniform = local->_groupStateSet->getOrCreateUniform( 
+        "oe_clamp_depthclip2eyemat", 
+        osg::Uniform::FLOAT_MAT4 );
+
+    // make the shader that will do clamping and depth offsetting.
+    VirtualProgram* vp = new VirtualProgram();
+    vp->setName( "ClampingBinTechnique program" );
+    local->_groupStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+
+    // vertex shader - subgraph
+    std::string vertexSource = Stringify()
+        << "#version " << GLSL_VERSION_STR << "\n"
+#ifdef OSG_GLES2_AVAILABLE
+        << "precision mediump float;\n"
+#endif
+        // uniforms from this ClampingBinTechnique:
+        << "uniform sampler2D oe_clamp_depthtex; \n"
+        << "uniform mat4 oe_clamp_eye2depthclipmat; \n"
+        << "uniform mat4 oe_clamp_depthclip2eyemat; \n"
+
+        // uniforms from ClampableNode:
+        << "uniform vec2 oe_clamp_bias; \n"
+        << "uniform vec2 oe_clamp_range; \n"
+
+        << "varying vec4 oe_clamp_simvert; \n"
+        << "varying float oe_clamp_simvertrange; \n"
+
+        << "void oe_clamp_vertex(void) \n"
+        << "{ \n"
+        // transform the vertex into the depth texture's clip coordinates.
+        << "    vec4 v_eye_orig = gl_ModelViewMatrix * gl_Vertex; \n"
+        << "    vec4 tc = oe_clamp_eye2depthclipmat * v_eye_orig; \n"
+
+        // sample the depth map.
+        << "    float d = texture2DProj( oe_clamp_depthtex, tc ).r; \n"
+
+        // make a fake point in depth clip space and transform it back into eye coords.
+        << "    vec4 p = vec4(tc.x, tc.y, d, 1.0); \n"
+        << "    vec4 v_eye_clamped = oe_clamp_depthclip2eyemat * p; \n"
+
+        // if the clamping distance is too big, bag it.
+        << "    vec3 v_eye_orig3    = v_eye_orig.xyz/v_eye_orig.w;\n"
+        << "    vec3 v_eye_clamped3 = v_eye_clamped.xyz/v_eye_clamped.w; \n"
+        << "    float clamp_distance = length(v_eye_orig3 - v_eye_clamped3); \n"
+
+        << "    const float maxClampDistance = 10000.0; \n"
+        
+        << "    if ( clamp_distance > maxClampDistance ) \n"
+        << "    { \n"
+        << "        gl_Position = gl_ProjectionMatrix * v_eye_orig; \n"
+        // still have to populate these to nullify the depth offset code.
+        << "        oe_clamp_simvert = gl_Position; \n"
+        << "        oe_clamp_simvertrange = 1.0; \n"
+        << "    } \n"
+        << "    else \n"
+        << "    { \n"
+
+        // now simulate a "closer" vertex for depth offsetting.
+
+        // remap depth offset based on camera distance to vertex. The farther you are away,
+        // the more of an offset you need.
+
+        << "        float range = length(v_eye_clamped3); \n"
+
+        << "        float ratio = (clamp(range, oe_clamp_range[0], oe_clamp_range[1])-oe_clamp_range[0])/(oe_clamp_range[1]-oe_clamp_range[0]);\n"
+        << "        float bias = oe_clamp_bias[0] + ratio * (oe_clamp_bias[1]-oe_clamp_bias[0]);\n"
+
+        << "        vec3 adj_vec = normalize(v_eye_clamped3); \n"
+        << "        vec3 v_eye_offset3 = v_eye_clamped3 - (adj_vec * bias); \n"
+
+        << "        vec4 v_sim_eye = vec4( v_eye_offset3 * v_eye_clamped.w, v_eye_clamped.w ); \n"
+        << "        oe_clamp_simvert = gl_ProjectionMatrix * v_sim_eye;\n"
+        << "        oe_clamp_simvertrange = range - bias; \n"
+        << "        gl_Position = gl_ProjectionMatrix * v_eye_clamped; \n"
+        << "    } \n"
+        << "} \n";
+
+    vp->setFunction( "oe_clamp_vertex", vertexSource, ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
+
+
+    // fragment shader - depth offset apply
+    std::string frag =
+        "varying vec4 oe_clamp_simvert; \n"
+        "varying float oe_clamp_simvertrange; \n"
+        "void oe_clamp_fragment(inout vec4 color)\n"
+        "{ \n"
+        "    float sim_depth = 0.5 * (1.0+(oe_clamp_simvert.z/oe_clamp_simvert.w));\n"
+
+        // if the offset pushed the Z behind the eye, the projection mapping will
+        // result in a z>1. We need to bring these values back down to the 
+        // near clip plan (z=0). We need to check simRange too before doing this
+        // so we don't draw fragments that are legitimently beyond the far clip plane.
+        "    if ( sim_depth > 1.0 && oe_clamp_simvertrange < 0.0 ) { sim_depth = 0.0; } \n"
+        "    gl_FragDepth = max(0.0, sim_depth); \n"
+        "}\n";
+
+    vp->setFunction( "oe_clamp_fragment", frag, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+}
+
+
+void
+ClampingBinTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
+                                  osgUtil::CullVisitor*             cv )
+{
+    if ( !params._rttCamera.valid() )
+    {
+        // set it up:
+        setUpCamera( params );
+
+        // store our camera's stateset in the perview data.
+        ClampingRenderBin* bin = dynamic_cast<ClampingRenderBin*>( osgUtil::RenderBin::getRenderBinPrototype(OSGEARTH_CLAMPING_BIN) );
+        if ( bin )
+        {
+            ClampingRenderBin::PerViewData& data = bin->_pvd->get( cv->getCurrentCamera() );
+            LocalPerViewData* local = static_cast<LocalPerViewData*>(params._techniqueData.get());
+            data._techData = local;
+        }
+        else
+        {
+            OE_WARN << LC << "Odd, no prototype found for the clamping bin." << std::endl;
+        }
+    }
+}
+
+
+void
+ClampingBinTechnique::cullOverlayGroup(OverlayDecorator::TechRTTParams& params,
+                                    osgUtil::CullVisitor*            cv )
+{
+    if ( params._rttCamera.valid() )
+    {
+        // update the RTT camera.
+        params._rttCamera->setViewMatrix      ( params._rttViewMatrix );
+        params._rttCamera->setProjectionMatrix( params._rttProjMatrix );
+
+        LocalPerViewData& local = *static_cast<LocalPerViewData*>(params._techniqueData.get());
+
+        // prime our CPM with the current cull visitor:
+        local._cpm->setup( cv );
+
+        // create the depth texture (render the terrain to tex)
+        params._rttCamera->accept( *cv );
+
+        // construct a matrix that transforms from camera view coords to depth texture
+        // clip coords directly. This will avoid precision loss in the 32-bit shader.
+        static osg::Matrix s_scaleBiasMat = 
+            osg::Matrix::translate(1.0,1.0,1.0) * 
+            osg::Matrix::scale(0.5,0.5,0.5);
+
+        osg::Matrix camViewToRttClip = 
+            cv->getCurrentCamera()->getInverseViewMatrix() * 
+            params._rttViewMatrix                          * 
+            local._cpm->_clampedProj                       *
+            s_scaleBiasMat;
+
+        local._camViewToDepthClipUniform->set( camViewToRttClip );
+        local._depthClipToCamViewUniform->set( osg::Matrix::inverse(camViewToRttClip) );
+
+        // GW: we dont' actually cull the overlay group. That will happen naturally.
+        // setting up all the uniforms is important though, and applying the stateset to
+        // the renderbin is too.
+    }
+}
+
+
+void
+ClampingBinTechnique::setTextureSize( int texSize )
+{
+    if ( texSize != _textureSize.value() )
+    {
+        _textureSize = texSize;
+    }
+}
+
+
+void
+ClampingBinTechnique::onInstall( TerrainEngineNode* engine )
+{
+    // save a pointer to the terrain engine.
+    _engine = engine;
+
+    if ( !_textureSize.isSet() )
+    {
+        unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
+        _textureSize.init( osg::minimum( 4096u, maxSize ) );
+
+        OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
+    }
+}
+
+
+void
+ClampingBinTechnique::onUninstall( TerrainEngineNode* engine )
+{
+    _engine = 0L;
+}
diff --git a/src/osgEarth/ClampingTechnique b/src/osgEarth/ClampingTechnique
new file mode 100644
index 0000000..d6ca973
--- /dev/null
+++ b/src/osgEarth/ClampingTechnique
@@ -0,0 +1,111 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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_OVERLAY_CLAMPING_TECHNIQUE
+#define OSGEARTH_OVERLAY_CLAMPING_TECHNIQUE
+
+#include <osgEarth/Common>
+#include <osgEarth/OverlayDecorator>
+#include <osg/TexGenNode>
+#include <osg/Uniform>
+
+#define OSGEARTH_CLAMPING_BIN "osgEarth::ClampingBin"
+
+namespace osgEarth
+{
+    class TerrainEngineNode;
+
+    /**
+     * Clamps geometry to the terrain using the GPU.
+     *
+     * This overlay technique installs a shader that samples the depth
+     * of the underlying terrain and then adjusts the vertex position to
+     * the height of the terrain. It then applies a depth offset in order
+     * to mitigate Z-fighting.
+     *
+     * Approach
+     *
+     * This technique works by first setting up a nadir-view RTT camera that
+     * captures the depth component of the terrain within the view frustum of
+     * the main camera. Then the GPU uses this "depth texture" to move each
+     * vertex so it coindices with the terrain.
+     *
+     * Limitations
+     *
+     * Since this is an orthographic view, the effective resolution of depth
+     * data close to the camera may not be quite sufficient, especially when
+     * the camera is pitched up looking out over the horizon. As a reuslt, you
+     * may see the verts "jitter" slightly in the Z direciton as you camera moves.
+     *
+     * Performance takes a hit since we need to RTT the terrain in a pre-render
+     * pass.
+     */
+    class OSGEARTH_EXPORT ClampingTechnique : public OverlayTechnique
+    {
+    public:
+        typedef osg::Group* (*TechniqueProvider)(class MapNode*);
+        static TechniqueProvider Provider;
+
+    public:
+        ClampingTechnique();
+
+        /**
+         * The size (resolution in both directions) of the depth map texture. By
+         * default, this defaults to 4096 or your hardware's maximum supported
+         * texture size, whichever is less.
+         */
+        void setTextureSize( int texSize );
+        int getTextureSize() const { return *_textureSize; }
+
+
+    public: // OverlayTechnique
+
+        bool hasData(
+            OverlayDecorator::TechRTTParams& params) const;
+
+        void reestablish(
+            TerrainEngineNode* engine );
+
+        void preCullTerrain(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv );
+
+        void cullOverlayGroup(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv );
+
+        void onInstall( TerrainEngineNode* engine );
+
+        void onUninstall( TerrainEngineNode* engine );
+
+
+    protected:
+        virtual ~ClampingTechnique() { }
+
+    private:
+        int                _textureUnit;
+        optional<int>      _textureSize;
+        TerrainEngineNode* _engine;
+
+    private:
+        void setUpCamera(OverlayDecorator::TechRTTParams& params);
+    };
+
+} // namespace osgEarth
+
+#endif //OSGEARTH_OVERLAY_CLAMPING_TECHNIQUE
diff --git a/src/osgEarth/ClampingTechnique.cpp b/src/osgEarth/ClampingTechnique.cpp
new file mode 100644
index 0000000..79cdb49
--- /dev/null
+++ b/src/osgEarth/ClampingTechnique.cpp
@@ -0,0 +1,575 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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/ClampingTechnique>
+#include <osgEarth/Capabilities>
+#include <osgEarth/CullingUtils>
+#include <osgEarth/Registry>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/MapNode>
+#include <osgEarth/Utils>
+
+#include <osg/Depth>
+#include <osg/PolygonMode>
+#include <osg/Texture2D>
+#include <osg/Uniform>
+
+#define LC "[ClampingTechnique] "
+
+//#define OE_TEST if (_dumpRequested) OE_INFO << std::setprecision(9)
+#define OE_TEST OE_NULL
+
+//#define USE_RENDER_BIN 1
+#undef USE_RENDER_BIN
+
+using namespace osgEarth;
+
+//---------------------------------------------------------------------------
+
+namespace
+{
+    osg::Group* s_providerImpl(MapNode* mapNode)
+    {
+        return mapNode ? mapNode->getOverlayDecorator()->getGroup<ClampingTechnique>() : 0L;
+    }
+}
+
+ClampingTechnique::TechniqueProvider ClampingTechnique::Provider = s_providerImpl;
+
+//--------------------------------------------------------------------------
+
+
+// SUPPORT_Z is a placeholder - we need to come up with another method for
+// clamping verts relative to Z without needing the current Model Matrix
+// (as we would now). Leave this #undef's until further notice.
+#define SUPPORT_Z 1
+#undef  SUPPORT_Z
+
+namespace
+{
+    const char clampingVertexShader[] =
+
+        "#version " GLSL_VERSION_STR "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n"
+
+         // uniforms from this ClampingTechnique:
+         "uniform sampler2D oe_clamp_depthTex; \n"
+         "uniform mat4 oe_clamp_cameraView2depthClip; \n"
+#ifdef SUPPORT_Z
+         "uniform mat4 oe_clamp_depthClip2depthView; \n"
+         "uniform mat4 oe_clamp_depthView2cameraView; \n"
+#else
+         "uniform mat4 oe_clamp_depthClip2cameraView; \n"
+#endif
+
+         "uniform float oe_clamp_horizonDistance; \n"
+
+         // uniforms from ClampableNode:
+         "uniform vec2 oe_clamp_bias; \n"
+         "uniform vec2 oe_clamp_range; \n"
+
+         "varying vec4 oe_clamp_simvert; \n"
+         "varying float oe_clamp_simvertrange; \n"
+         "varying float oe_clamp_alphaFactor; \n"
+
+         "void oe_clamp_vertex(inout vec4 VertexVIEW) \n"
+         "{ \n"
+         //   start by mocing the vertex into view space.
+         "    vec4 v_view_orig = VertexVIEW; \n"
+
+         //   if the distance to the vertex is beyond the visible horizon,
+         //   "hide" the vertex by setting its alpha component to 0.0.
+         //   if this is the case, there's no point in continuing -- so we 
+         //   would normally branch here, but since this happens on the fly 
+         //   the shader engine will run both branches regardless. So keep going.
+         "    float vert_distance = length(v_view_orig.xyz/v_view_orig.w); \n"
+         "    oe_clamp_alphaFactor = clamp(oe_clamp_horizonDistance - vert_distance, 0.0, 1.0 ); \n"
+
+         //   transform the vertex into the depth texture's clip coordinates.
+         "    vec4 v_depthClip = oe_clamp_cameraView2depthClip * v_view_orig; \n"
+
+         //   sample the depth map.
+         "    float d = texture2DProj( oe_clamp_depthTex, v_depthClip ).r; \n"
+
+         //   now transform into depth-view space so we can apply the height-above-ground:
+         "    vec4 p_depthClip = vec4(v_depthClip.x, v_depthClip.y, d, 1.0); \n"
+
+#ifdef SUPPORT_Z
+         "    vec4 p_depthView = oe_clamp_depthClip2depthView * p_depthClip; \n"
+
+              // next, apply the vert's Z value for that ground offset.
+              // TODO: This calculation is not right!
+              //       I think perhaps the model matrix is not earth-aligned.
+         "    p_depthView.z += gl_Vertex.z*gl_Vertex.w/p_depthView.w; \n"
+
+              // then transform the vert back into camera view space.
+         "    vec4 v_view_clamped = oe_clamp_depthView2cameraView * p_depthView; \n"
+#else
+              // transform the depth-clip point back into camera view coords.
+         "    vec4 v_view_clamped = oe_clamp_depthClip2cameraView * p_depthClip; \n"
+#endif
+
+
+         //   now simulate a "closer" vertex for depth offsetting.
+         //   remap depth offset based on camera distance to vertex. The farther you are away,
+         //   the more of an offset you need.
+
+         //   calculate the range to target:
+         "    vec3 v_view_clamped3 = v_view_clamped.xyz/v_view_clamped.w; \n"
+         "    float range = length(v_view_clamped3); \n"
+
+         //   calculate the depth offset bias for this range:
+         "    float ratio = (clamp(range, oe_clamp_range[0], oe_clamp_range[1])-oe_clamp_range[0])/(oe_clamp_range[1]-oe_clamp_range[0]);\n"
+         "    float bias = oe_clamp_bias[0] + ratio * (oe_clamp_bias[1]-oe_clamp_bias[0]);\n"
+
+         //   calculate the "simluated" vertex:
+         "    vec3 adj_vec = normalize(v_view_clamped3); \n"
+         "    vec3 v_view_offset3 = v_view_clamped3 - (adj_vec * bias); \n"
+
+         "    vec4 v_view_sim = vec4( v_view_offset3 * v_view_clamped.w, v_view_clamped.w ); \n"
+         "    oe_clamp_simvert = gl_ProjectionMatrix * v_view_sim;\n"
+         "    oe_clamp_simvertrange = range - bias; \n"
+
+         "    VertexVIEW = v_view_clamped; \n"
+         //"    gl_Position = gl_ProjectionMatrix * v_view_clamped; \n"
+         "} \n";
+
+
+    const char clampingFragmentShader[] =
+
+        "#version " GLSL_VERSION_STR "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n"
+
+        "varying vec4 oe_clamp_simvert; \n"
+        "varying float oe_clamp_simvertrange; \n"
+        "varying float oe_clamp_alphaFactor; \n"
+
+        "void oe_clamp_fragment(inout vec4 color)\n"
+        "{ \n"
+        "    float sim_depth = 0.5 * (1.0+(oe_clamp_simvert.z/oe_clamp_simvert.w));\n"
+
+             // if the offset pushed the Z behind the eye, the projection mapping will
+             // result in a z>1. We need to bring these values back down to the 
+             // near clip plan (z=0). We need to check simRange too before doing this
+             // so we don't draw fragments that are legitimently beyond the far clip plane.
+        "    if ( sim_depth > 1.0 && oe_clamp_simvertrange < 0.0 ) { sim_depth = 0.0; } \n"
+        "    gl_FragDepth = max(0.0, sim_depth); \n"
+
+             // adjust the alpha component to "hide" geometry beyond the visible horizon.
+        "    color.a *= oe_clamp_alphaFactor; \n"
+        "}\n";
+
+}
+
+//---------------------------------------------------------------------------
+
+namespace
+{
+    // Additional per-view data stored by the clamping technique.
+    struct LocalPerViewData : public osg::Referenced
+    {
+        osg::ref_ptr<osg::Texture2D> _rttTexture;
+        osg::ref_ptr<osg::StateSet>  _groupStateSet;
+        osg::ref_ptr<osg::Uniform>   _camViewToDepthClipUniform;
+        osg::ref_ptr<osg::Uniform>   _depthClipToCamViewUniform;
+
+        osg::ref_ptr<osg::Uniform>   _depthClipToDepthViewUniform;
+        osg::ref_ptr<osg::Uniform>   _depthViewToCamViewUniform;
+
+        osg::ref_ptr<osg::Uniform>   _horizonDistanceUniform;
+
+        unsigned _renderLeafCount;
+    };
+}
+
+#ifdef USE_RENDER_BIN
+
+//---------------------------------------------------------------------------
+// Custom bin for clamping.
+
+namespace
+{
+    struct ClampingRenderBin : public osgUtil::RenderBin
+    {
+        struct PerViewData // : public osg::Referenced
+        {
+            osg::observer_ptr<LocalPerViewData> _techData;
+        };
+
+        // shared across ALL render bin instances.
+        typedef Threading::PerObjectMap<osg::Camera*, PerViewData> PerViewDataMap;
+        PerViewDataMap* _pvd; 
+
+        // support cloning (from RenderBin):
+        virtual osg::Object* cloneType() const { return new ClampingRenderBin(); }
+        virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new ClampingRenderBin(*this,copyop); } // note only implements a clone of type.
+        virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const ClampingRenderBin*>(obj)!=0L; }
+        virtual const char* libraryName() const { return "osgEarth"; }
+        virtual const char* className() const { return "ClampingRenderBin"; }
+
+
+        // constructs the prototype for this render bin.
+        ClampingRenderBin() : osgUtil::RenderBin()
+        {
+            this->setName( OSGEARTH_CLAMPING_BIN );
+            _pvd = new PerViewDataMap();
+        }
+
+        ClampingRenderBin( const ClampingRenderBin& rhs, const osg::CopyOp& op )
+            : osgUtil::RenderBin( rhs, op ), _pvd( rhs._pvd )
+        {
+            // zero out the stateset...dont' want to share that!
+            _stateset = 0L;
+        }
+
+        // override.
+        void sortImplementation()
+        {
+            copyLeavesFromStateGraphListToRenderLeafList();
+        }
+
+        // override.
+        void drawImplementation(osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
+        {
+            // find and initialize the state set for this camera.
+            if ( !_stateset.valid() )
+            {
+                osg::Camera* camera = renderInfo.getCurrentCamera();
+                PerViewData& data = _pvd->get(camera);
+                if ( data._techData.valid() )
+                {
+                    _stateset = data._techData->_groupStateSet.get();
+                    LocalPerViewData* local = static_cast<LocalPerViewData*>(data._techData.get());
+                    local->_renderLeafCount = _renderLeafList.size();
+                }
+            }
+
+            osgUtil::RenderBin::drawImplementation( renderInfo, previous );
+        }
+    };
+}
+
+/** the static registration. */
+extern "C" void osgEarth_clamping_bin_registration(void) {}
+static osgEarthRegisterRenderBinProxy<ClampingRenderBin> s_regbin(OSGEARTH_CLAMPING_BIN);
+
+#endif // USE_RENDER_BIN
+
+//---------------------------------------------------------------------------
+
+ClampingTechnique::ClampingTechnique() :
+_textureSize( 4096 )
+{
+    // disable if GLSL is not supported
+    _supported = Registry::capabilities().supportsGLSL();
+
+    // use the maximum available unit.
+    _textureUnit = Registry::capabilities().getMaxGPUTextureUnits() - 1;
+}
+
+
+bool
+ClampingTechnique::hasData(OverlayDecorator::TechRTTParams& params) const
+{
+#ifdef USE_RENDER_BIN
+    //TODO: reconsider
+    return true;
+#else
+    return params._group->getNumChildren() > 0;
+#endif
+}
+
+
+void
+ClampingTechnique::reestablish(TerrainEngineNode* engine)
+{
+    // nop.
+}
+
+
+void
+ClampingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
+{
+    // To store technique-specific per-view info:
+    LocalPerViewData* local = new LocalPerViewData();
+    params._techniqueData = local;
+
+    // create the projected texture:
+    local->_rttTexture = new osg::Texture2D();
+    local->_rttTexture->setTextureSize( *_textureSize, *_textureSize );
+    local->_rttTexture->setInternalFormat( GL_DEPTH_COMPONENT );
+    local->_rttTexture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST );
+    local->_rttTexture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
+
+    // this is important. geometry that is outside the depth texture will clamp to the
+    // closest edge value in the texture -- this is good when you are rendering a 
+    // primitive that has one or more of its verts off-screen.
+    local->_rttTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE );
+    local->_rttTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
+    //local->_rttTexture->setBorderColor( osg::Vec4(0,0,0,1) );
+
+    // set up the RTT camera:
+    params._rttCamera = new osg::Camera();
+    params._rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
+    params._rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
+    params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+    params._rttCamera->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
+    params._rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize );
+    params._rttCamera->setRenderOrder( osg::Camera::PRE_RENDER );
+    params._rttCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
+    params._rttCamera->attach( osg::Camera::DEPTH_BUFFER, local->_rttTexture.get() );
+
+    // set up a StateSet for the RTT camera.
+    osg::StateSet* rttStateSet = params._rttCamera->getOrCreateStateSet();
+
+    // lighting is off. We don't want draped items to be lit.
+    //rttStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
+
+    rttStateSet->setMode(
+        GL_BLEND, 
+        osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
+
+    // prevents wireframe mode in the depth camera.
+    rttStateSet->setAttributeAndModes(
+        new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL ),
+        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+
+#if 0 //OOPS this kills things like a vertical scale shader!!
+    // installs a dirt-simple program for rendering the depth texture that
+    // skips all the normal terrain rendering stuff
+    osg::Program* depthProg = new osg::Program();
+    depthProg->addShader(new osg::Shader(
+        osg::Shader::VERTEX, 
+        "void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; }\n"));
+    depthProg->addShader(new osg::Shader(
+        osg::Shader::FRAGMENT, 
+        "void main() { gl_FragColor = vec4(1,1,1,1); }\n"));
+    rttStateSet->setAttributeAndModes(
+        depthProg,
+        osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED );
+#endif
+    
+    // attach the terrain to the camera.
+    // todo: should probably protect this with a mutex.....
+    params._rttCamera->addChild( _engine ); //params._terrainParent->getChild(0) ); // the terrain itself.
+
+    // assemble the overlay graph stateset.
+    local->_groupStateSet = new osg::StateSet();
+
+    local->_groupStateSet->setTextureAttributeAndModes( 
+        _textureUnit, 
+        local->_rttTexture.get(), 
+        osg::StateAttribute::ON );
+
+#if 1
+    // set up depth test/write parameters for the overlay geometry:
+    local->_groupStateSet->setAttributeAndModes(
+        new osg::Depth( osg::Depth::LEQUAL, 0.0, 1.0, true ),
+        osg::StateAttribute::ON );
+#endif
+
+    local->_groupStateSet->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
+
+    // uniform for the horizon distance (== max clamping distance)
+    local->_horizonDistanceUniform = local->_groupStateSet->getOrCreateUniform(
+        "oe_clamp_horizonDistance",
+        osg::Uniform::FLOAT );
+
+    // sampler for depth map texture:
+    local->_groupStateSet->getOrCreateUniform(
+        "oe_clamp_depthTex", 
+        osg::Uniform::SAMPLER_2D )->set( _textureUnit );
+
+    // matrix that transforms a vert from EYE coords to the depth camera's CLIP coord.
+    local->_camViewToDepthClipUniform = local->_groupStateSet->getOrCreateUniform( 
+        "oe_clamp_cameraView2depthClip", 
+        osg::Uniform::FLOAT_MAT4 );
+
+#ifdef SUPPORT_Z
+
+    // matrix that transforms a vert from depth clip coords to depth view coords.
+    local->_depthClipToDepthViewUniform = local->_groupStateSet->getOrCreateUniform(
+        "oe_clamp_depthClip2depthView",
+        osg::Uniform::FLOAT_MAT4 );
+
+    // matrix that transforms a vert from depth view coords to camera view coords.
+    local->_depthViewToCamViewUniform = local->_groupStateSet->getOrCreateUniform(
+        "oe_clamp_depthView2cameraView",
+        osg::Uniform::FLOAT_MAT4 );
+
+#else
+
+    // matrix that transforms a vert from depth-cam CLIP coords to EYE coords.
+    local->_depthClipToCamViewUniform = local->_groupStateSet->getOrCreateUniform( 
+        "oe_clamp_depthClip2cameraView", 
+        osg::Uniform::FLOAT_MAT4 );
+
+#endif
+
+    // make the shader that will do clamping and depth offsetting.
+    VirtualProgram* vp = new VirtualProgram();
+    vp->setName( "ClampingTechnique" );
+    vp->setFunction( "oe_clamp_vertex",   clampingVertexShader,   ShaderComp::LOCATION_VERTEX_VIEW );
+    vp->setFunction( "oe_clamp_fragment", clampingFragmentShader, ShaderComp::LOCATION_FRAGMENT_COLORING );
+    local->_groupStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+}
+
+
+void
+ClampingTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
+                                 osgUtil::CullVisitor*             cv )
+{
+    if ( !params._rttCamera.valid() && hasData(params) )
+    {
+        setUpCamera( params );
+
+#ifdef USE_RENDER_BIN
+
+        // store our camera's stateset in the perview data.
+        ClampingRenderBin* bin = dynamic_cast<ClampingRenderBin*>( osgUtil::RenderBin::getRenderBinPrototype(OSGEARTH_CLAMPING_BIN) );
+        if ( bin )
+        {
+            ClampingRenderBin::PerViewData& data = bin->_pvd->get( cv->getCurrentCamera() );
+            LocalPerViewData* local = static_cast<LocalPerViewData*>(params._techniqueData.get());
+            data._techData = local;
+        }
+        else
+        {
+            OE_WARN << LC << "Odd, no prototype found for the clamping bin." << std::endl;
+        }
+
+#endif
+    }
+}
+
+
+void
+ClampingTechnique::cullOverlayGroup(OverlayDecorator::TechRTTParams& params,
+                                    osgUtil::CullVisitor*            cv )
+{
+    if ( params._rttCamera.valid() && hasData(params) )
+    {
+        // update the RTT camera.
+        params._rttCamera->setViewMatrix      ( params._rttViewMatrix );
+        params._rttCamera->setProjectionMatrix( params._rttProjMatrix );
+
+        LocalPerViewData& local = *static_cast<LocalPerViewData*>(params._techniqueData.get());
+
+        // prime our CPM with the current cull visitor:
+        //local._cpm->setup( cv );
+
+        // create the depth texture (render the terrain to tex)
+        params._rttCamera->accept( *cv );
+
+        // construct a matrix that transforms from camera view coords to depth texture
+        // clip coords directly. This will avoid precision loss in the 32-bit shader.
+        static osg::Matrix s_scaleBiasMat = 
+            osg::Matrix::translate(1.0,1.0,1.0) * 
+            osg::Matrix::scale    (0.5,0.5,0.5);
+
+        static osg::Matrix s_invScaleBiasMat = osg::Matrix::inverse(
+            osg::Matrix::translate(1.0,1.0,1.0) * 
+            osg::Matrix::scale    (0.5,0.5,0.5) );
+
+        osg::Matrix cameraViewToDepthView =
+            cv->getCurrentCamera()->getInverseViewMatrix() * 
+            params._rttViewMatrix;
+
+        osg::Matrix depthViewToDepthClip = 
+            //local._cpm->_clampedDepthProjMatrix *
+            params._rttProjMatrix *
+            s_scaleBiasMat;
+
+        osg::Matrix cameraViewToDepthClip =
+            cameraViewToDepthView *
+            depthViewToDepthClip;
+        local._camViewToDepthClipUniform->set( cameraViewToDepthClip );
+
+        local._horizonDistanceUniform->set( float(*params._horizonDistance) );
+
+        //OE_NOTICE << "HD = " << std::setprecision(8) << float(*params._horizonDistance) << std::endl;
+
+#if SUPPORT_Z
+        osg::Matrix depthClipToDepthView;
+        depthClipToDepthView.invert( depthViewToDepthClip );
+        local._depthClipToDepthViewUniform->set( depthClipToDepthView );
+
+        osg::Matrix depthViewToCameraView;
+        depthViewToCameraView.invert( cameraViewToDepthView );
+        local._depthViewToCamViewUniform->set( depthViewToCameraView );
+#else
+
+        osg::Matrix depthClipToCameraView;
+        depthClipToCameraView.invert( cameraViewToDepthClip );
+        local._depthClipToCamViewUniform->set( depthClipToCameraView );
+#endif
+
+        if ( params._group->getNumChildren() > 0 )
+        {
+            // traverse the overlay nodes, applying the clamping shader.
+            cv->pushStateSet( local._groupStateSet.get() );
+
+            // Since the vertex shader is moving the verts to clamp them to the terrain,
+            // OSG will not be able to properly cull the geometry. (Specifically: OSG may
+            // cull geometry which is invisible when NOT clamped, but becomes visible after
+            // GPU clamping.) We work around that by using a Proxy cull visitor that will 
+            // use the RTT camera's matrixes for frustum culling (instead of the main camera's).
+            ProxyCullVisitor pcv( cv, params._rttProjMatrix, params._rttViewMatrix );
+
+            // cull the clampable geometry.
+            params._group->accept( pcv );
+            //params._group->accept( *cv ); // old way - direct traversal
+
+            // done; pop the clamping shaders.
+            cv->popStateSet();
+        }
+    }
+}
+
+
+void
+ClampingTechnique::setTextureSize( int texSize )
+{
+    if ( texSize != _textureSize.value() )
+    {
+        _textureSize = texSize;
+    }
+}
+
+
+void
+ClampingTechnique::onInstall( TerrainEngineNode* engine )
+{
+    // save a pointer to the terrain engine.
+    _engine = engine;
+
+    if ( !_textureSize.isSet() )
+    {
+        unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
+        _textureSize.init( osg::minimum( 4096u, maxSize ) );
+
+        OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
+    }
+}
+
+
+void
+ClampingTechnique::onUninstall( TerrainEngineNode* engine )
+{
+    _engine = 0L;
+}
diff --git a/src/osgEarth/ColorFilter b/src/osgEarth/ColorFilter
index e7c8427..458c35e 100644
--- a/src/osgEarth/ColorFilter
+++ b/src/osgEarth/ColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ColorFilter.cpp b/src/osgEarth/ColorFilter.cpp
index 835ea9e..30e2540 100644
--- a/src/osgEarth/ColorFilter.cpp
+++ b/src/osgEarth/ColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Common b/src/osgEarth/Common
index c9789aa..bf6d6e9 100644
--- a/src/osgEarth/Common
+++ b/src/osgEarth/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CompositeTileSource b/src/osgEarth/CompositeTileSource
index f5393fd..062c807 100644
--- a/src/osgEarth/CompositeTileSource
+++ b/src/osgEarth/CompositeTileSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CompositeTileSource.cpp b/src/osgEarth/CompositeTileSource.cpp
index 8c7ab12..8bd01f5 100644
--- a/src/osgEarth/CompositeTileSource.cpp
+++ b/src/osgEarth/CompositeTileSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Config b/src/osgEarth/Config
index 1bdc175..4d7cb6d 100644
--- a/src/osgEarth/Config
+++ b/src/osgEarth/Config
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Config.cpp b/src/osgEarth/Config.cpp
index 44008b7..072c4e9 100644
--- a/src/osgEarth/Config.cpp
+++ b/src/osgEarth/Config.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Containers b/src/osgEarth/Containers
index b40b40b..8de45ae 100644
--- a/src/osgEarth/Containers
+++ b/src/osgEarth/Containers
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -121,20 +121,26 @@ namespace osgEarth
     {
     public:
         struct Record {
-            Record(const T* value) : _value(value) { }
-            const bool valid() const { return _value != 0L; }
-            const T& value() const { return *_value; }
+            Record() : _valid(false) { }
+            Record(const T& value) : _value(value), _valid(true) { }
+            const bool valid() const { return _valid; }
+            const T& value() const { return _value; }
+            //Record(const T* value) : _value(value) { }
+            //const bool valid() const { return _value != 0L; }
+            //const T& value() const { return *_value; }
         private:
             bool _valid;
-            const T* _value;
+            T    _value;
+            friend class LRUCache;
+            //const T* _value;
         };
 
     protected:
-        typedef typename std::list<K>::iterator lru_iter;
-        typedef typename std::list<K> lru_type;
-        typedef typename std::pair<T, lru_iter> map_value_type;
+        typedef typename std::list<K>::iterator      lru_iter;
+        typedef typename std::list<K>                lru_type;
+        typedef typename std::pair<T, lru_iter>      map_value_type;
         typedef typename std::map<K, map_value_type> map_type;
-        typedef typename map_type::iterator map_iter;
+        typedef typename map_type::iterator          map_iter;
 
         map_type _map;
         lru_type _lru;
@@ -170,14 +176,15 @@ namespace osgEarth
             }
         }
 
-        Record get( const K& key ) {
+        bool get( const K& key, Record& out ) {
             if ( _threadsafe ) {
                 Threading::ScopedMutexLock lock(_mutex);
-                return get_impl( key );
+                get_impl( key, out );
             }
             else {
-                return get_impl( key );
+                get_impl( key, out );
             }
+            return out.valid();
         }
 
         bool has( const K& key ) {
@@ -255,7 +262,7 @@ namespace osgEarth
             }
         }
 
-        Record get_impl( const K& key ) {
+        void get_impl( const K& key, Record& result ) {
             _queries++;
             map_iter mi = _map.find( key );
             if ( mi != _map.end() ) {
@@ -264,11 +271,13 @@ namespace osgEarth
                 lru_iter new_iter = _lru.end(); new_iter--;
                 mi->second.second = new_iter;
                 _hits++;
-                return Record( &(mi->second.first) );
-            }
-            else {
-                return Record( 0L );
+                result._value = mi->second.first;
+                result._valid = true;
+                //return Record( &(mi->second.first) );
             }
+            //else {
+            //    return Record( 0L );
+            //}
         }
 
         bool has_impl( const K& key ) {
diff --git a/src/osgEarth/Cube b/src/osgEarth/Cube
index e4898b5..3622a2a 100644
--- a/src/osgEarth/Cube
+++ b/src/osgEarth/Cube
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Cube.cpp b/src/osgEarth/Cube.cpp
index 18e5e71..fe34101 100644
--- a/src/osgEarth/Cube.cpp
+++ b/src/osgEarth/Cube.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/CullingUtils b/src/osgEarth/CullingUtils
index 682032c..06432cf 100644
--- a/src/osgEarth/CullingUtils
+++ b/src/osgEarth/CullingUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -21,9 +21,11 @@
 #define OSGEARTH_CULLING_UTILS_H 1
 
 #include <osgEarth/Common>
+#include <osgEarth/optional>
 #include <osg/NodeCallback>
 #include <osg/ClusterCullingCallback>
 #include <osg/CoordinateSystemNode>
+#include <osg/MatrixTransform>
 #include <osg/Vec3d>
 #include <osg/Vec3>
 #include <osgUtil/CullVisitor>
@@ -92,10 +94,22 @@ namespace osgEarth
         }
     };
 
+    /**
+     * Culler that tests whether the line-of-sight vector between the camera
+     * and the node intersects the Ellipsoid, and if so, culls the node.
+     */
+    struct OSGEARTH_EXPORT CullNodeByEllipsoid : public osg::NodeCallback {
+        double _minRadius;
+        CullNodeByEllipsoid( const osg::EllipsoidModel* model );
+        void operator()(osg::Node* node, osg::NodeVisitor* nv);
+    };
+
     struct OSGEARTH_EXPORT CullNodeByHorizon : public osg::NodeCallback {
+        osg::observer_ptr<osg::MatrixTransform> _xform;
         osg::Vec3d _world;
         double _r, _r2;
         CullNodeByHorizon( const osg::Vec3d& world, const osg::EllipsoidModel* model );
+        CullNodeByHorizon( osg::MatrixTransform* xform, const osg::EllipsoidModel* model );
         void operator()(osg::Node* node, osg::NodeVisitor* nv );
     };
 
@@ -140,7 +154,63 @@ namespace osgEarth
         double _maxRange2;
     };
 
+    /**
+     * Culling utilities.
+     */
+    struct OSGEARTH_EXPORT Culling
+    {
+        static osgUtil::CullVisitor* asCullVisitor(osg::NodeVisitor* nv);
+        static osgUtil::CullVisitor* asCullVisitor(osg::NodeVisitor& nv) { return asCullVisitor(&nv); }
+
+        /** General user data to calculate once and pass down with a CullVisitor. */
+        struct CullUserData : public osg::Referenced
+        {
+            optional<double> _cameraAltitude;
+        };
 
+        static CullUserData* getCullUserData(osgUtil::CullVisitor* cv) {
+            return cv ? dynamic_cast<CullUserData*>(cv->getUserData()) : 0L; }
+    };
+
+    /**
+     * Node Visitor that proxies the CullVisitor but uses a separate
+     * frustum for bounds-culling.
+     */
+    class ProxyCullVisitor : public osg::NodeVisitor, public osg::CullStack
+    {
+    private:
+        osgUtil::CullVisitor* _cv;
+        osg::Polytope         _proxyFrustum;
+        osg::Polytope         _proxyProjFrustum;
+        osg::Matrix           _proxyModelViewMatrix;
+        osg::Matrix           _proxyProjMatrix;
+
+    public:
+        ProxyCullVisitor( osgUtil::CullVisitor* cv, const osg::Matrix& proj, const osg::Matrix& view );
+
+        // access to the underlying cull visitor.
+        osgUtil::CullVisitor* getCullVisitor() { return _cv; }
+
+    public: // proxy functions:
+        osg::Vec3 getEyePoint() const;
+        osg::Vec3 getViewPoint() const;
+        float getDistanceToEyePoint(const osg::Vec3& pos, bool useLODScale) const;
+        float getDistanceFromEyePoint(const osg::Vec3& pos, bool useLODScale) const;
+        float getDistanceToViewPoint(const osg::Vec3& pos, bool useLODScale) const;
+
+    protected: // custom culling functions:
+
+        bool isCulledByProxyFrustum(osg::Node& node);
+        bool isCulledByProxyFrustum(const osg::BoundingBox& bbox);
+        
+        osgUtil::CullVisitor::value_type distance(const osg::Vec3& coord,const osg::Matrix& matrix);
+
+        void handle_cull_callbacks_and_traverse(osg::Node& node);
+
+        void apply(osg::Node& node);
+        void apply(osg::Transform& node);
+        void apply(osg::Geode& node);
+    };
 }
 
 #endif // OSGEARTH_CULLING_UTILS_H
diff --git a/src/osgEarth/CullingUtils.cpp b/src/osgEarth/CullingUtils.cpp
index a30ec69..c004581 100644
--- a/src/osgEarth/CullingUtils.cpp
+++ b/src/osgEarth/CullingUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -288,10 +288,31 @@ namespace
 //----------------------------------------------------------------------------
 
 
+osgUtil::CullVisitor*
+Culling::asCullVisitor(osg::NodeVisitor* nv)
+{
+    if ( !nv )
+        return 0L;
+
+    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( nv );
+    if ( cv )
+        return cv;
+
+    ProxyCullVisitor* pcv = dynamic_cast<ProxyCullVisitor*>( nv );
+    if ( pcv )
+        return pcv->getCullVisitor();
+
+    return 0L;
+}
+
+
+//----------------------------------------------------------------------------
+
+
 bool 
 SuperClusterCullingCallback::cull(osg::NodeVisitor* nv, osg::Drawable* , osg::State*) const
 {
-    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
+    osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
 
     if (!cv) return false;
 
@@ -419,10 +440,69 @@ ClusterCullingFactory::create(const osg::Vec3& controlPoint,
 
 //------------------------------------------------------------------------
 
+CullNodeByEllipsoid::CullNodeByEllipsoid( const osg::EllipsoidModel* model ) :
+_minRadius( std::min(model->getRadiusPolar(), model->getRadiusEquator()) )
+{
+    //nop
+}
+
+
+void
+CullNodeByEllipsoid::operator()(osg::Node* node, osg::NodeVisitor* nv)
+{
+    if ( nv )
+    {
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
+
+        // camera location
+        osg::Vec3d vp, center, up;
+        cv->getCurrentCamera()->getViewMatrixAsLookAt(vp, center, up);
+        double vpLen2 = vp.length2();
+
+        // world bound of this model
+        osg::Matrix l2w = osg::computeLocalToWorld( nv->getNodePath() );
+        const osg::BoundingSphere& bs = node->getBound();
+        osg::BoundingSphere bsWorld( bs.center() * l2w, bs.radius() * l2w.getScale().x() );
+        double bswLen2 = bsWorld.center().length2();
+
+        double vpLen = vp.length();
+        osg::Vec3d vpToTarget = bsWorld.center() - vp;
+        
+        vp.normalize();
+        vpToTarget.normalize();
+        double theta = acos( vpToTarget * -vp );
+        double r = vpLen * sin(theta);
+        //double p = 
+
+        // "r" is the length of the shortest line between the center of the 
+        // ellipsoid and the line of light. If (r) is less than the ellipsoid's
+        // minumum radius, that means the ellipsoid is blocking the LOS.
+        // (We "tweak" r a bit: increase it by the target object's radius so we
+        // can account for the whole object, and subtract the lower point on 
+        // earth to account for the camera being underground)
+        if ( r + bsWorld.radius() > _minRadius - 11000.0 )
+        {
+            OE_NOTICE 
+                << "r=" << r << ", rad="<<bsWorld.radius()<<", min=" << _minRadius
+                << std::endl;
+            traverse(node, nv);
+        }
+    }
+}
+
+//------------------------------------------------------------------------
+
 CullNodeByHorizon::CullNodeByHorizon( const osg::Vec3d& world, const osg::EllipsoidModel* model ) :
 _world(world),
-_r(model->getRadiusPolar()),
-_r2(model->getRadiusPolar() * model->getRadiusPolar())
+_r    (model->getRadiusPolar()),
+_r2   (model->getRadiusPolar() * model->getRadiusPolar())
+{
+    //nop
+}
+CullNodeByHorizon::CullNodeByHorizon( osg::MatrixTransform* xform, const osg::EllipsoidModel* model ) :
+_xform(xform),
+_r    (model->getRadiusPolar()),
+_r2   (model->getRadiusPolar() * model->getRadiusPolar())
 {
     //nop
 }
@@ -432,18 +512,20 @@ CullNodeByHorizon::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
     if ( nv )
     {
-        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( nv );
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
 
         // get the viewpoint. It will be relative to the current reference location (world).
         osg::Matrix l2w = osg::computeLocalToWorld( nv->getNodePath(), true );
         osg::Vec3d vp  = cv->getViewPoint() * l2w;
 
+        osg::Vec3d world = _xform.valid() ? _xform->getMatrix().getTrans() : _world;
+
         // same quadrant:
-        if ( vp * _world >= 0.0 )
+        if ( vp * world >= 0.0 )
         {
             double d2 = vp.length2();
             double horiz2 = d2 - _r2;
-            double dist2 = (_world-vp).length2();
+            double dist2 = (world-vp).length2();
             if ( dist2 < horiz2 )
             {
                 traverse(node, nv);
@@ -456,8 +538,8 @@ CullNodeByHorizon::operator()(osg::Node* node, osg::NodeVisitor* nv)
             // there's a horizon between them; now see if the thing is visible.
             // find the triangle formed by the viewpoint, the target point, and 
             // the center of the earth.
-            double a = (_world-vp).length();
-            double b = _world.length();
+            double a = (world-vp).length();
+            double b = world.length();
             double c = vp.length();
 
             // Heron's formula for triangle area:
@@ -487,7 +569,7 @@ void
 CullNodeByNormal::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
     osg::Vec3d eye, center, up;
-    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( nv );
+    osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
 
     cv->getCurrentCamera()->getViewMatrixAsLookAt(eye,center,up);
 
@@ -507,12 +589,9 @@ CullNodeByNormal::operator()(osg::Node* node, osg::NodeVisitor* nv)
 void
 DisableSubgraphCulling::operator()(osg::Node* n, osg::NodeVisitor* v)
 {
-    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(v);
+    osgUtil::CullVisitor* cv = Culling::asCullVisitor(v);
     cv->getCurrentCullingSet().setCullingMask( osg::CullSettings::NO_CULLING );
-    //osg::CullSettings::ComputeNearFarMode mode = cv->getComputeNearFarMode();
-    //cv->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
     traverse(n, v);
-    //cv->setComputeNearFarMode( mode );
 }
 
 
@@ -553,7 +632,7 @@ void OcclusionCullingCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
     if (nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
     {
         osg::Vec3d eye, center, up;
-        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( nv );
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
 
         cv->getCurrentCamera()->getViewMatrixAsLookAt(eye,center,up);
 
@@ -567,7 +646,8 @@ void OcclusionCullingCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
                 osg::Vec3d start = eye;
                 osg::Vec3d end = _world;
                 osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
-                osgUtil::IntersectionVisitor iv;            
+                i->setIntersectionLimit( osgUtil::Intersector::LIMIT_ONE );
+                osgUtil::IntersectionVisitor iv;
                 iv.setIntersector( i );
                 _node->accept( iv );
                 osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
@@ -592,3 +672,233 @@ void OcclusionCullingCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
         traverse( node, nv );
     }
 }
+
+//------------------------------------------------------------------------
+
+ProxyCullVisitor::ProxyCullVisitor( osgUtil::CullVisitor* cv, const osg::Matrix& proj, const osg::Matrix& view ) :
+osg::NodeVisitor( osg::NodeVisitor::CULL_VISITOR, osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN ),
+_cv             ( cv )
+{
+    // set up the initial proxy frustum for culling:
+    _proxyProjFrustum.setToUnitFrustum( true, true );
+    _proxyProjFrustum.transformProvidingInverse( proj );
+    _proxyModelViewMatrix = view;
+    _proxyFrustum.setAndTransformProvidingInverse( _proxyProjFrustum, view );
+
+    // copy NodeVisitor values over to this visitor:
+    _nodePath = _cv->getNodePath();
+
+    this->setFrameStamp( const_cast<osg::FrameStamp*>(_cv->getFrameStamp()) );
+    this->setTraversalNumber( _cv->getTraversalNumber() );
+    this->setTraversalMask( _cv->getTraversalMask() );
+    this->setNodeMaskOverride( _cv->getNodeMaskOverride() );
+    this->setDatabaseRequestHandler( _cv->getDatabaseRequestHandler() );
+    this->setImageRequestHandler( _cv->getImageRequestHandler() );
+    this->setUserData( _cv->getUserData() );
+    this->setComputeNearFarMode( _cv->getComputeNearFarMode() );
+}
+
+osg::Vec3 
+ProxyCullVisitor::getEyePoint() const { 
+    return _cv->getEyePoint();
+}
+
+osg::Vec3 
+ProxyCullVisitor::getViewPoint() const { 
+    return _cv->getViewPoint();
+}
+
+float 
+ProxyCullVisitor::getDistanceToEyePoint(const osg::Vec3& pos, bool useLODScale) const { 
+    return _cv->getDistanceToEyePoint(pos, useLODScale);
+}
+
+float 
+ProxyCullVisitor::getDistanceFromEyePoint(const osg::Vec3& pos, bool useLODScale) const {
+    return _cv->getDistanceFromEyePoint(pos, useLODScale);
+}
+
+float 
+ProxyCullVisitor::getDistanceToViewPoint(const osg::Vec3& pos, bool useLODScale) const { 
+    return _cv->getDistanceToViewPoint(pos, useLODScale);
+}
+
+bool 
+ProxyCullVisitor::isCulledByProxyFrustum(osg::Node& node)
+{
+    return node.isCullingActive() && !_proxyFrustum.contains(node.getBound());
+}
+
+bool 
+ProxyCullVisitor::isCulledByProxyFrustum(const osg::BoundingBox& bbox)
+{
+    return !_proxyFrustum.contains(bbox);
+}
+
+osgUtil::CullVisitor::value_type 
+ProxyCullVisitor::distance(const osg::Vec3& coord,const osg::Matrix& matrix)
+{
+    return -((osgUtil::CullVisitor::value_type)coord[0]*(osgUtil::CullVisitor::value_type)matrix(0,2)+(osgUtil::CullVisitor::value_type)coord[1]*(osgUtil::CullVisitor::value_type)matrix(1,2)+(osgUtil::CullVisitor::value_type)coord[2]*(osgUtil::CullVisitor::value_type)matrix(2,2)+matrix(3,2));
+}
+
+void 
+ProxyCullVisitor::handle_cull_callbacks_and_traverse(osg::Node& node)
+{
+    osg::NodeCallback* callback = node.getCullCallback();
+    if (callback) (*callback)(&node,this);
+    else traverse(node);
+}
+
+void 
+ProxyCullVisitor::apply(osg::Node& node)
+{
+    //OE_INFO << "Node: " << node.className() << std::endl;
+
+    if ( isCulledByProxyFrustum(node) )
+        return;
+
+    _cv->pushOntoNodePath( &node );
+
+    _cv->pushCurrentMask();
+    osg::StateSet* node_state = node.getStateSet();
+    if (node_state) _cv->pushStateSet(node_state);
+    handle_cull_callbacks_and_traverse(node);
+    if (node_state) _cv->popStateSet();
+    _cv->popCurrentMask();
+
+    _cv->popFromNodePath();
+}
+
+void 
+ProxyCullVisitor::apply(osg::Transform& node)
+{
+    //OE_INFO << "Transform!" << std::endl;
+
+    if ( isCulledByProxyFrustum(node) )
+        return;
+
+    _cv->pushOntoNodePath( &node);
+
+    _cv->pushCurrentMask();
+    osg::StateSet* node_state = node.getStateSet();
+    if (node_state) _cv->pushStateSet(node_state);
+
+    // push the current proxy data:
+    osg::Polytope savedF  = _proxyFrustum;
+    osg::Matrix   savedMV = _proxyModelViewMatrix;
+
+    // calculate the new proxy frustum:
+    node.computeLocalToWorldMatrix(_proxyModelViewMatrix, this);
+    _proxyFrustum.setAndTransformProvidingInverse( _proxyProjFrustum, _proxyModelViewMatrix );
+
+    osg::ref_ptr<osg::RefMatrix> matrix = createOrReuseMatrix(*_cv->getModelViewMatrix());
+    node.computeLocalToWorldMatrix(*matrix,this);
+    _cv->pushModelViewMatrix(matrix.get(), node.getReferenceFrame());
+
+    // traverse children:
+    handle_cull_callbacks_and_traverse(node);
+
+    // restore the previous proxy frustum and MVM
+    _proxyFrustum         = savedF;
+    _proxyModelViewMatrix = savedMV;
+
+    _cv->popModelViewMatrix();
+    if (node_state) _cv->popStateSet();
+    _cv->popCurrentMask();
+
+    _cv->popFromNodePath();
+}
+
+void 
+ProxyCullVisitor::apply(osg::Geode& node)
+{
+    //OE_INFO << "Geode!" << std::endl;
+
+    if ( isCulledByProxyFrustum(node) )
+        return;
+
+    _cv->pushOntoNodePath( &node );
+
+    // push the node's state.
+    osg::StateSet* node_state = node.getStateSet();
+    if (node_state) _cv->pushStateSet(node_state);
+
+    // traverse any call callbacks and traverse any children.
+    handle_cull_callbacks_and_traverse(node);
+
+    osg::RefMatrix& matrix = *_cv->getModelViewMatrix();
+    for(unsigned int i=0;i<node.getNumDrawables();++i)
+    {
+        osg::Drawable* drawable = node.getDrawable(i);
+        const osg::BoundingBox& bb =drawable->getBound();
+
+        if( drawable->getCullCallback() )
+        {
+            if( drawable->getCullCallback()->cull( _cv, drawable, &_cv->getRenderInfo() ) == true )
+                continue;
+        }
+
+        //else
+        {
+            if (node.isCullingActive() && isCulledByProxyFrustum(bb)) continue;
+        }
+
+
+        if ( _cv->getComputeNearFarMode() && bb.valid())
+        {
+            if (!_cv->updateCalculatedNearFar(matrix,*drawable,false)) continue;
+        }
+
+        // need to track how push/pops there are, so we can unravel the stack correctly.
+        unsigned int numPopStateSetRequired = 0;
+
+        // push the geoset's state on the geostate stack.
+        osg::StateSet* stateset = drawable->getStateSet();
+        if (stateset)
+        {
+            ++numPopStateSetRequired;
+            _cv->pushStateSet(stateset);
+        }
+
+        osg::CullingSet& cs = _cv->getCurrentCullingSet();
+        if (!cs.getStateFrustumList().empty())
+        {
+            osg::CullingSet::StateFrustumList& sfl = cs.getStateFrustumList();
+            for(osg::CullingSet::StateFrustumList::iterator itr = sfl.begin();
+                itr != sfl.end();
+                ++itr)
+            {
+                if (itr->second.contains(bb))
+                {
+                    ++numPopStateSetRequired;
+                    _cv->pushStateSet(itr->first.get());
+                }
+            }
+        }
+
+        float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f;
+
+        if (osg::isNaN(depth))
+        {
+            for (osg::NodePath::const_iterator i = getNodePath().begin(); i != getNodePath().end(); ++i)
+            {
+                OSG_DEBUG << "        \"" << (*i)->getName() << "\"" << std::endl;
+            }
+        }
+        else
+        {
+            _cv->addDrawableAndDepth(drawable,&matrix,depth);
+        }
+
+        for(unsigned int i=0;i< numPopStateSetRequired; ++i)
+        {
+            _cv->popStateSet();
+        }
+
+    }
+
+    // pop the node's state off the geostate stack.
+    if (node_state) _cv->popStateSet();
+
+    _cv->popFromNodePath();
+}
diff --git a/src/osgEarth/DPLineSegmentIntersector b/src/osgEarth/DPLineSegmentIntersector
index cc81365..fe21186 100644
--- a/src/osgEarth/DPLineSegmentIntersector
+++ b/src/osgEarth/DPLineSegmentIntersector
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/DPLineSegmentIntersector.cpp b/src/osgEarth/DPLineSegmentIntersector.cpp
index 4018cd0..225e244 100644
--- a/src/osgEarth/DPLineSegmentIntersector.cpp
+++ b/src/osgEarth/DPLineSegmentIntersector.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/DepthOffset b/src/osgEarth/DepthOffset
index ae97593..aa432e7 100644
--- a/src/osgEarth/DepthOffset
+++ b/src/osgEarth/DepthOffset
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -21,28 +21,97 @@
 #define OSGEARTH_DEPTH_ADJUSTMENT_H 1
 
 #include <osgEarth/Common>
+#include <osgEarth/Config>
 #include <osg/Group>
 #include <osg/Program>
 #include <osg/Uniform>
 
 /**
-* Depth offsetting mitigates z-fighting artifacts for geometry that is very close
-* together, e.g. terrain-following lines. It uses a shader program to apply a 
-* simulated eye-space offset, writing values to the z-buffer as if the geometry were
-* drawn closer to the camera than they actually are. The effect is similar to what
-* glPolygonOffset is *supposed* to do (but doesn't). You get rid of z-fighting but
-* still maintain local occlusion.
+* Depth Offsetting.
+* 
+* Geometry that coincides with the terrain can result in z-fighting artifacts.
+* Depth offsetting mitigates this by biasing the depth value of the geometry.
+* The idea is similar to polygon offsetting, but is dynamic and applies to all
+* geometry (not just polygons).
 *
-* There is a trade off. You need to set a "minimum offset" that the technique will
-* apply to the geometry. From there, it uses distance-to-vertex to ramp between that and
-* a set maximum offset. The appropriate minimum offset depends a lot on the tessellation
-* of your geometry, so it's wise to use a higher offset for geometry with longer 
-* segments when in a round-earth situation. The automatic setting (the default) analyzes
-* the subgraph and picks a decent value, but you can always override this is you like.
+* Depth offsetting works by pretending the vertex is closer to the camera 
+* than it actually is, and writing a depth value based on that simulated
+* location. The distance we shift the vertex towards the camera is the "bias".
+*
+* The "range" is the distance from camera to vertex at which a given
+* bias is applied. The minimum bias is applied to geometry at or below the
+* minimum range; the maximum bias is applied to geometry at or above the 
+* maximum range; and the bias is interpolated for ranges in between.
+*
+* The tessellation granularity of the geometry affects how well depth offsetting
+* works at a given camera distance. As a rule of thumb, the closer the camera is
+* to the geometry, the more it needs to be tessellated in order for depth
+* offsetting to work properly.
 */
 namespace osgEarth
 {
     /**
+    * Depth Offsetting options.
+    */
+    class OSGEARTH_EXPORT DepthOffsetOptions
+    {
+    public:
+        DepthOffsetOptions(const Config& conf =Config());
+
+    public:
+        /** whether to enable depth offsetting (when applicable) */
+        optional<bool>& enabled() { return _enabled; }
+        const optional<bool>& enabled() const { return _enabled; }
+
+        /** depth bias (in meters) applied at the minimum camera range. */
+        optional<float>& minBias() { return _minBias; }
+        const optional<float>& minBias() const { return _minBias; }
+
+        /** depth bias (in meters) applied at the maximum camera range. */
+        optional<float>& maxBias() { return _maxBias; }
+        const optional<float>& maxBias() const { return _maxBias; }
+
+        /** camera range (in meters) at which to apply the minimum depth bias. */
+        optional<float>& minRange() { return _minRange; }
+        const optional<float>& minRange() const { return _minRange; }
+
+        /** camera range (in meters) at which to apply the maximum depth bias. */
+        optional<float>& maxRange() { return _maxRange; }
+        const optional<float>& maxRange() const { return _maxRange; }
+
+    public:
+        Config getConfig() const;
+
+    private:
+        optional<bool>  _enabled;
+        optional<float> _minBias;
+        optional<float> _maxBias;
+        optional<float> _minRange;
+        optional<float> _maxRange;
+    };
+
+
+    /**
+     * Controller that affects a stateset with depth offset settings.
+     * It does NOT install any shaders.
+     */
+    class OSGEARTH_EXPORT DepthOffsetOptionsAdapter
+    {
+    public:
+        DepthOffsetOptionsAdapter(osg::StateSet* stateSet);
+
+        void setOptions(const DepthOffsetOptions& options);
+        const DepthOffsetOptions& getOptions() const { return _options; }
+
+    private:
+        osg::ref_ptr<osg::StateSet> _stateSet;
+        osg::ref_ptr<osg::Uniform>  _biasUniform;
+        osg::ref_ptr<osg::Uniform>  _rangeUniform;
+        DepthOffsetOptions          _options;
+    };
+
+
+    /**
      * Utilities to manage depth testing for feature data. Handy especially
      * for terrain-conforming lines.
      */
@@ -63,7 +132,7 @@ namespace osgEarth
          * returns it. Also may install support uniforms within the graph as
          * necessary to support depth offsetting.
          */
-        static float recalculate( osg::Node* graph );
+        static float recalculate( const osg::Node* graph );
 
         /**
          * Traverses a graph and applies the necessary uniforms to statesets
diff --git a/src/osgEarth/DepthOffset.cpp b/src/osgEarth/DepthOffset.cpp
index f7e8d4a..ba7995f 100644
--- a/src/osgEarth/DepthOffset.cpp
+++ b/src/osgEarth/DepthOffset.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -44,22 +44,97 @@ using namespace osgEarth;
 
 //------------------------------------------------------------------------
 
+
+DepthOffsetOptions::DepthOffsetOptions(const Config& conf) :
+_enabled ( true ),
+_minBias (      100.0f ),
+_maxBias (    10000.0f ),
+_minRange(     1000.0f ),
+_maxRange( 10000000.0f )
+{
+    conf.getIfSet( "enabled",   _enabled );
+    conf.getIfSet( "min_bias",  _minBias );
+    conf.getIfSet( "max_bias",  _maxBias );
+    conf.getIfSet( "min_range", _minRange );
+    conf.getIfSet( "max_range", _maxRange );
+}
+
+
+Config
+DepthOffsetOptions::getConfig() const
+{
+    Config conf("depth_offset");
+    conf.addIfSet( "enabled",   _enabled );
+    conf.addIfSet( "min_bias",  _minBias );
+    conf.addIfSet( "max_bias",  _maxBias );
+    conf.addIfSet( "min_range", _minRange );
+    conf.addIfSet( "max_range", _maxRange );
+    return conf;
+}
+
+
+//------------------------------------------------------------------------
+
+
+DepthOffsetOptionsAdapter::DepthOffsetOptionsAdapter(osg::StateSet* stateSet) :
+_stateSet( stateSet )
+{
+    if ( _stateSet.valid() )
+    {
+        _biasUniform = _stateSet->getOrCreateUniform( "oe_clamp_bias", osg::Uniform::FLOAT_VEC2 );
+        _biasUniform->set( osg::Vec2f(*_options.minBias(), *_options.maxBias()) );
+
+        _rangeUniform = _stateSet->getOrCreateUniform( "oe_clamp_range", osg::Uniform::FLOAT_VEC2 );
+        _rangeUniform->set( osg::Vec2f(*_options.minRange(), *_options.maxRange()) );
+    }
+}
+
+
+void 
+DepthOffsetOptionsAdapter::setOptions(const DepthOffsetOptions& options)
+{
+    _options = options;
+
+    if ( _stateSet.valid() )
+    {
+        _biasUniform->set( osg::Vec2f(*_options.minBias(), *_options.maxBias()) );
+        _rangeUniform->set( osg::Vec2f(*_options.minRange(), *_options.maxRange()) );
+    }
+}
+
+
+//------------------------------------------------------------------------
+
+
 namespace
 {
     struct SegmentAnalyzer
     {
-        SegmentAnalyzer() : _maxLen2(0) { }
+        SegmentAnalyzer() : _maxLen2(0), _segmentsAnalyzed(0) { }
         void operator()( const osg::Vec3& v0, const osg::Vec3& v1, bool ) {
             double len2 = (v1-v0).length2();
             if ( len2 > _maxLen2 ) _maxLen2 = len2;
+            _segmentsAnalyzed++;
         }
         double _maxLen2;
+        int    _segmentsAnalyzed;
     };
 
     struct GeometryAnalysisVisitor : public osg::NodeVisitor
     {
         GeometryAnalysisVisitor()
-            : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _analyzeSegments(true) { }
+            : osg::NodeVisitor     (osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+              _analyzeSegments     (true),
+              _applyTextUniforms   (false),
+              _maxSegmentsToAnalyze(250) { }
+
+        void apply( osg::Node& node )
+        {
+            if ( _segmentAnalyzer._segmentsAnalyzed < _maxSegmentsToAnalyze )
+            {
+                traverse(node);
+            }
+        }
 
         void apply( osg::Geode& geode )
         {
@@ -71,14 +146,18 @@ namespace
                 {
                     d->accept( _segmentAnalyzer );
                 }
-                else if ( dynamic_cast<osgText::Text*>(d) )
+                else if ( _applyTextUniforms && dynamic_cast<osgText::Text*>(d) )
                 {
                     d->getOrCreateStateSet()->addUniform( DepthOffsetUtils::getIsTextUniform() );
                 }
             }
+            traverse((osg::Node&)geode);
         }
+
         LineFunctor<SegmentAnalyzer> _segmentAnalyzer;
+        int                          _maxSegmentsToAnalyze;
         bool                         _analyzeSegments;
+        bool                         _applyTextUniforms;
     };
 
 
@@ -248,14 +327,14 @@ DepthOffsetUtils::createMinOffsetUniform( osg::Node* graph )
 }
 
 float
-DepthOffsetUtils::recalculate( osg::Node* graph )
+DepthOffsetUtils::recalculate( const osg::Node* graph )
 {
     double minDepthOffset = 0.0;
     if ( graph )
     {
         GeometryAnalysisVisitor v;
         v._analyzeSegments = true;
-        graph->accept( v );
+        const_cast<osg::Node*>(graph)->accept( v );
         double maxLen = std::max(1.0, sqrt(v._segmentAnalyzer._maxLen2));
         minDepthOffset = sqrt(maxLen)*19.0;
 
@@ -278,6 +357,7 @@ DepthOffsetUtils::prepareGraph( osg::Node* graph )
 
 namespace
 {
+    // dubious. refactor static-init stuff away please.
     static osg::ref_ptr<osg::Uniform> s_isTextUniform;
     static Threading::Mutex           s_isTextUniformMutex;
 }
@@ -299,6 +379,7 @@ DepthOffsetUtils::getIsTextUniform()
 
 namespace
 {    
+    // please refactor away the static-init stuff.
     static osg::ref_ptr<osg::Uniform> s_isNotTextUniform;
     static Threading::Mutex           s_isNotTextUniformMutex;
 }
@@ -320,6 +401,7 @@ DepthOffsetUtils::getIsNotTextUniform()
  
 namespace
 {
+    // todo: refactor away the static init stuff
     static osg::ref_ptr<osg::Program> s_depthOffsetProgram;
     static Threading::Mutex           s_depthOffsetProgramMutex;
 }
diff --git a/src/osgEarth/Draggers b/src/osgEarth/Draggers
index d629b7e..aaf930b 100644
--- a/src/osgEarth/Draggers
+++ b/src/osgEarth/Draggers
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -26,6 +26,7 @@
 #include <osg/MatrixTransform>
 #include <osg/ShapeDrawable>
 #include <osgGA/GUIEventHandler>
+#include <osgManipulator/Projector>
 #include <osgEarth/Terrain>
 
 namespace osgEarth
@@ -48,7 +49,13 @@ namespace osgEarth
 
         typedef std::list< osg::ref_ptr<PositionChangedCallback> > PositionChangedCallbackList;
 
-        Dragger( MapNode* mapNode);
+        enum DragMode
+        {
+          DRAGMODE_HORIZONTAL,
+          DRAGMODE_VERTICAL
+        };
+
+        Dragger( MapNode* mapNode, int modKeyMask=0, const DragMode& defaultMode=DRAGMODE_HORIZONTAL );
 
         /** dtor */
         virtual ~Dragger();
@@ -61,6 +68,18 @@ namespace osgEarth
 
         void setPosition( const osgEarth::GeoPoint& position, bool fireEvents=true);
 
+        void setModKeyMask(int mask) { _modKeyMask = mask; }
+
+        int getModKeyMask() const { return _modKeyMask; }
+
+        void setDefaultDragMode(const DragMode& mode) { _defaultMode = mode; }
+
+        DragMode& getDefaultDragMode() { return _defaultMode; }
+
+        void setVerticalMinimum(double min) { _verticalMinimum = min; }
+
+        double getVerticalMinimim() const { return _verticalMinimum; }
+
 
         void updateTransform(osg::Node* patch = 0);
 
@@ -100,6 +119,14 @@ namespace osgEarth
         bool _dragging;
         bool _hovered;
         PositionChangedCallbackList _callbacks;
+
+        osg::ref_ptr<  osgManipulator::LineProjector >  _projector;
+        osgManipulator::PointerInfo  _pointer;
+        osg::Vec3d _startProjectedPoint;
+        bool _elevationDragging;
+        int _modKeyMask;
+        DragMode _defaultMode;
+        double _verticalMinimum;
     };
 
     /**********************************************************/
diff --git a/src/osgEarth/Draggers.cpp b/src/osgEarth/Draggers.cpp
index 4b25b74..ff19665 100644
--- a/src/osgEarth/Draggers.cpp
+++ b/src/osgEarth/Draggers.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -27,6 +27,8 @@
 
 #include <osgGA/EventVisitor>
 
+#include <osgManipulator/Dragger>
+
 
 
 using namespace osgEarth;
@@ -47,15 +49,20 @@ Dragger* _dragger;
 };
 
 /**********************************************************/
-Dragger::Dragger( MapNode* mapNode):
+Dragger::Dragger( MapNode* mapNode, int modKeyMask, const DragMode& defaultMode ):
 _mapNode( mapNode ),
 _position( mapNode->getMapSRS(), 0,0,0, ALTMODE_RELATIVE),
 _dragging(false),
-_hovered(false)
+_hovered(false),
+_modKeyMask(modKeyMask),
+_defaultMode(defaultMode),
+_elevationDragging(false),
+_verticalMinimum(0.0)
 {
     setNumChildrenRequiringEventTraversal( 1 );
 
     _autoClampCallback = new ClampDraggerCallback( this );
+    _projector = new osgManipulator::LineProjector;
 
     setMapNode( mapNode );
 }
@@ -189,21 +196,120 @@ bool Dragger::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&
         if ( picker.pick( ea.getX(), ea.getY(), hits ) )
         {
             _dragging = true;
+
+            //Check for and handle vertical dragging if necessary
+            bool pressedAlt = _modKeyMask && (ea.getModKeyMask() & _modKeyMask) > 0;
+            _elevationDragging = (_defaultMode == Dragger::DRAGMODE_VERTICAL && !pressedAlt) || (_defaultMode == Dragger::DRAGMODE_HORIZONTAL && pressedAlt);
+
+            if (_elevationDragging)
+            {
+              _pointer.reset();
+
+              // set movement range
+              // TODO: values 0.0 and 300000.0 are rather experimental
+              GeoPoint posStart(_position.getSRS(), _position.x(), _position.y(), 0.0, ALTMODE_ABSOLUTE);
+              osg::Vec3d posStartXYZ;
+              posStart.toWorld(posStartXYZ);
+
+              GeoPoint posEnd(_position.getSRS(), _position.x(), _position.y(), 300000.0, ALTMODE_ABSOLUTE);
+              osg::Vec3d posEndXYZ;
+              posEnd.toWorld(posEndXYZ);
+
+              _projector->setLine(posStartXYZ, posEndXYZ);
+
+              // set camera
+              osgUtil::LineSegmentIntersector::Intersections intersections;
+              osg::Node::NodeMask intersectionMask = 0xffffffff;
+              osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
+              if (view->computeIntersections(ea.getX(),ea.getY(),intersections, intersectionMask))
+              {
+                  for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr)
+                  {
+                      _pointer.addIntersection(hitr->nodePath, hitr->getLocalIntersectPoint());
+                  }
+
+                  bool draggerFound = false;
+                  for (osgManipulator::PointerInfo::IntersectionList::iterator piit = _pointer._hitList.begin(); piit != _pointer._hitList.end(); ++piit)
+                  {
+                      for (osg::NodePath::iterator itr = piit->first.begin(); itr != piit->first.end(); ++itr)
+                      {
+                          Dragger* dragger = dynamic_cast<Dragger*>(*itr);
+                          if (dragger==this)
+                          {
+                            draggerFound = true;
+                              osg::Camera *rootCamera = view->getCamera();
+                              osg::NodePath nodePath = _pointer._hitList.front().first;
+                              osg::NodePath::reverse_iterator ritr;
+                              for (ritr = nodePath.rbegin(); ritr != nodePath.rend(); ++ritr)
+                              {
+                                  osg::Camera* camera = dynamic_cast<osg::Camera*>(*ritr);
+                                  if (camera && (camera->getReferenceFrame()!=osg::Transform::RELATIVE_RF || camera->getParents().empty()))
+                                  {
+                                       rootCamera = camera;
+                                       break;
+                                  }
+                              }
+                              _pointer.setCamera(rootCamera);
+                              _pointer.setMousePosition(ea.getX(), ea.getY());
+
+                              break;
+                          }
+                      }
+
+                      if (draggerFound)
+                        break;
+                  }
+              }
+            }
+
             aa.requestRedraw();
             return true;
         }
     }
     else if (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE)
     {
+        _elevationDragging = false;
+
         if ( _dragging )
         {
             _dragging = false;
             firePositionChanged();
         }
+
         aa.requestRedraw();
     }
     else if (ea.getEventType() == osgGA::GUIEventAdapter::DRAG)
     {
+        if (_elevationDragging) 
+        {
+            _pointer._hitIter = _pointer._hitList.begin();
+            _pointer.setMousePosition(ea.getX(), ea.getY());
+
+            if (_projector->project(_pointer, _startProjectedPoint)) 
+            {
+                //Get the absolute mapPoint that they've drug it to.
+                GeoPoint projectedPos;
+                projectedPos.fromWorld(_position.getSRS(), _startProjectedPoint);
+
+                // make sure point is not dragged down below
+                // TODO: think of a better solution / HeightAboveTerrain performance issues?
+                if (projectedPos.z() >= _verticalMinimum)
+                {
+                    //If the current position is relative, we need to convert the absolute world point to relative.
+                    //If the point is absolute then just emit the absolute point.
+                    if (_position.altitudeMode() == ALTMODE_RELATIVE)
+                    {
+                        projectedPos.transformZ(ALTMODE_RELATIVE, getMapNode()->getTerrain());
+                    }
+
+                    setPosition( projectedPos );
+                    aa.requestRedraw();
+                }
+            }
+
+            return true;
+        }
+        
         if (_dragging)
         {
             osg::Vec3d world;
@@ -221,6 +327,7 @@ bool Dragger::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&
                     mapPoint.alt() = _position.alt();
                     mapPoint.altitudeMode() = ALTMODE_RELATIVE;
                 }
+
                 setPosition( mapPoint );
                 aa.requestRedraw();
                 return true;
@@ -278,7 +385,7 @@ void Dragger::reclamp( const TileKey& key, osg::Node* tile, const Terrain* terra
 /**********************************************************/
 
 SphereDragger::SphereDragger(MapNode* mapNode):
-Dragger( mapNode ),
+Dragger(mapNode),
 _pickColor(1.0f, 1.0f, 0.0f, 1.0f),
 _color(0.0f, 1.0f, 0.0f, 1.0f),
 _size( 5.0f )
@@ -322,7 +429,6 @@ void SphereDragger::setColor(const osg::Vec4f& color)
     }
 }
 
-
 const osg::Vec4f& SphereDragger::getPickColor() const
 {
     return _pickColor;
@@ -351,7 +457,6 @@ void SphereDragger::setSize(float size)
     }
 }
 
-
 void SphereDragger::enter()
 {
     updateColor();
@@ -373,3 +478,4 @@ void SphereDragger::updateColor()
         _shapeDrawable->setColor( _color );
     }
 }
+
diff --git a/src/osgEarth/DrapeableNode b/src/osgEarth/DrapeableNode
index 00bdfcd..bb51f13 100644
--- a/src/osgEarth/DrapeableNode
+++ b/src/osgEarth/DrapeableNode
@@ -21,7 +21,7 @@
 #define OSGEARTH_DRAPEABLE_NODE_H 1
 
 #include <osgEarth/Common>
-#include <osgEarth/MapNodeObserver>
+#include <osgEarth/OverlayNode>
 #include <osg/Group>
 
 namespace osgEarth
@@ -35,49 +35,21 @@ namespace osgEarth
      * Usage: Create this node and put it anywhere in the scene graph. The
      * subgraph of this node will be draped on the MapNode's terrain.
      */
-    class OSGEARTH_EXPORT DrapeableNode : public osg::Group, public MapNodeObserver
+    class OSGEARTH_EXPORT DrapeableNode : public OverlayNode
     {
     public:
         /**
          * Constructs a new drapeable node.
          */
-        DrapeableNode( MapNode* mapNode, bool draped =true );
+        DrapeableNode( MapNode* mapNode, bool active =true );
 
+        /** Backwards compatibility */
+        void setDraped( bool value ) { setActive(value); }
+        bool getDraped() const { return getActive(); }
+
+    protected:
         /** dtor */
         virtual ~DrapeableNode() { }
-
-        /**
-         * Whether to drape the node content on the mapnode terrain.
-         */
-        void setDraped( bool value );
-        bool getDraped() const { return _draped; }
-
-    public: // MapNodeObserver
-
-        void setMapNode( MapNode* mapNode );
-
-        MapNode* getMapNode() { return _mapNode.get(); }
-
-    public: // osg::Node
-
-        virtual void traverse( osg::NodeVisitor& nv );
-
-    public: // osg::Group
-
-        // override these in order to manage the proxy container.
-        virtual bool addChild( osg::Node* child );
-        virtual bool insertChild( unsigned index, osg::Node* child );
-        virtual bool removeChild( osg::Node* child );
-        virtual bool replaceChild( osg::Node* origChild, osg::Node* newChild );
-
-    private:
-        bool                       _draped;
-        bool                       _dirty;
-        bool                       _newDraped;
-        osg::ref_ptr<osg::Group>   _overlayProxyContainer;
-        osg::observer_ptr<MapNode> _mapNode;
-
-        void applyChanges();
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/DrapeableNode.cpp b/src/osgEarth/DrapeableNode.cpp
index 1d59d5f..b456446 100644
--- a/src/osgEarth/DrapeableNode.cpp
+++ b/src/osgEarth/DrapeableNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,278 +18,29 @@
  */
 
 #include <osgEarth/DrapeableNode>
-#include <osgEarth/CullingUtils>
 #include <osgEarth/OverlayDecorator>
+#include <osgEarth/DrapingTechnique>
 #include <osgEarth/MapNode>
-#include <osgEarth/NodeUtils>
-#include <osgUtil/IntersectionVisitor>
 
 #define LC "[DrapeableNode] "
 
 using namespace osgEarth;
 
-namespace
-{
-    /**
-     * 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 ) 
-            : _owner(owner) { }
-
-        void traverse(osg::NodeVisitor& nv)
-        {
-            // only allow CULL and OD-internal traversal:
-            if ( dynamic_cast<OverlayDecorator::InternalNodeVisitor*>(&nv) )
-            {
-                osg::Group::traverse( nv );
-            }
-            else if ( nv.getVisitorType() == nv.CULL_VISITOR && _owner.valid() )
-            {
-                // first find the highest ancestor in the owner's node parental node path that does
-                // not occur in the visitor's node path. That is where we want to begin collecting
-                // state.
-                const osg::NodePath& visitorPath = nv.getNodePath();
-
-                // get the owner's node path (just use the first one)
-                osg::NodePathList ownerPaths;
-                ownerPaths = _owner->getParentalNodePaths();
-
-                // note: I descovered that getParentalNodePaths will stop when it finds an "invalid"
-                // node mask (e.g., == zero).. so indeed it's possible for there to be zero node paths.
-                if ( ownerPaths.size() > 0 )
-                {
-                    const osg::NodePath& ownerPath = ownerPaths[0];
-
-                    // first check the owner's traversal mask.
-                    bool visible = true;
-                    for( int k = 0; visible && k < (int)ownerPath.size(); ++k )
-                    {
-                        visible = nv.validNodeMask(*ownerPath[k]);
-                    }
-
-                    if ( visible )
-                    {
-                        // find the intersection point:
-                        int i = findIndexOfNodePathConvergence( visitorPath, ownerPath );
-
-                        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 < (int)ownerPath.size(); ++k )
-                            {
-                                osg::Node* node = ownerPath[k];
-                                osg::StateSet* ss = ownerPath[k]->getStateSet();
-                                if ( ss )
-                                {
-                                    cv->pushStateSet( ss );
-                                    ++pushes;
-                                }
-                            }
-                        
-                            osg::Group::traverse( nv );
-
-                            for( int k = 0; k < pushes; ++k )
-                            {
-                                cv->popStateSet();
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-
-        // returns the deepest index into the ownerPath at which the two paths converge
-        // (i.e. share a node pointer)
-        int findIndexOfNodePathConvergence(const osg::NodePath& visitorPath, const osg::NodePath& ownerPath)
-        {
-            // use the knowledge that a NodePath is a vector.
-
-            for( int vi = visitorPath.size()-1; vi >= 0; --vi )
-            {
-                osg::Node* visitorNode = visitorPath[vi];
-                for( int oi = ownerPath.size()-1; oi >= 0; --oi )
-                {
-                    if ( ownerPath[oi] == visitorNode )
-                    {
-                        // found the deepest intersection, so set the start index to one higher.
-                        return oi;
-                    }
-                }
-            }   
-
-            // no convergence. 
-            return -1;
-        }
-
-        osg::observer_ptr<osg::Node> _owner;
-    };
-}
-
 //------------------------------------------------------------------------
 
-DrapeableNode::DrapeableNode( MapNode* mapNode, bool draped ) :
-_newDraped( draped ),
-_draped   ( false ),
-_dirty    ( false )
-{
-    // create a container group that will house the culler. This culler
-    // allows a draped node, which sits under the MapNode's OverlayDecorator,
-    // to "track" the traversal state of the DrapeableNode itself.
-    _overlayProxyContainer = new OverlayProxy( this );
-
-    setMapNode( mapNode );
-
-    if ( mapNode )
-    {
-        // If draping is requested, set up to apply it on the first update traversal.
-        // Can't apply it until then since we need safe access to the MapNode.
-        setDraped( draped );
-    }
-    else
-    {
-        OE_DEBUG << LC << "Creates a drapeable without a MapNode; draping will be disabled" << std::endl;
-    }
-}
-
-void
-DrapeableNode::setMapNode( MapNode* mapNode )
-{
-    MapNode* oldMapNode = getMapNode();
-
-    if ( oldMapNode != mapNode )
-    {
-        if ( oldMapNode && _draped && _overlayProxyContainer->getNumParents() > 0 )
-        {
-            oldMapNode->getOverlayGroup()->removeChild( _overlayProxyContainer.get() );
-            oldMapNode->updateOverlayGraph();
-        }
-
-        _mapNode = mapNode;
-
-        applyChanges();
-    }
-}
-
-void
-DrapeableNode::applyChanges()
+namespace
 {
-    _draped = _newDraped;
-
-    if ( getMapNode() )
+    static osg::Group* getTechniqueGroup(MapNode* m)
     {
-        if ( _draped && _overlayProxyContainer->getNumParents() == 0 )
-        {
-            getMapNode()->getOverlayGroup()->addChild( _overlayProxyContainer.get() );
-            getMapNode()->updateOverlayGraph();
-        }
-        else if ( !_draped && _overlayProxyContainer->getNumParents() > 0 )
-        {
-            getMapNode()->getOverlayGroup()->removeChild( _overlayProxyContainer.get() );
-            getMapNode()->updateOverlayGraph();
-        }
-
-        dirtyBound();
-    }
-}
-
-void
-DrapeableNode::setDraped( bool draped )
-{    
-    if ( draped != _draped && getMapNode() )
-    {        
-        _newDraped = draped;
-        if ( !_dirty )
-        {
-            _dirty = true;
-            ADJUST_UPDATE_TRAV_COUNT( this, 1 );
-        }        
+        return m ? m->getOverlayDecorator()->getGroup<DrapingTechnique>() : 0L;
     }
 }
 
-bool
-DrapeableNode::addChild( osg::Node* child )
-{
-    bool ok = osg::Group::addChild( child );
-    if ( _overlayProxyContainer.valid() )
-        _overlayProxyContainer->addChild( child );
-    return ok;
-}
-
-bool
-DrapeableNode::insertChild( unsigned i, osg::Node* child )
-{
-    bool ok = osg::Group::insertChild( i, child );
-    if ( _overlayProxyContainer.valid() )
-        _overlayProxyContainer->insertChild( i, child );
-    return ok;
-}
-
-bool
-DrapeableNode::removeChild( osg::Node* child )
-{
-    bool ok = osg::Group::removeChild( child );
-    if ( _overlayProxyContainer.valid() )
-        _overlayProxyContainer->removeChild( child );
-    return ok;
-}
+//------------------------------------------------------------------------
 
-bool
-DrapeableNode::replaceChild( osg::Node* oldChild, osg::Node* newChild )
+DrapeableNode::DrapeableNode( MapNode* mapNode, bool draped ) :
+OverlayNode( mapNode, draped, &getTechniqueGroup )
 {
-    bool ok = osg::Group::replaceChild( oldChild, newChild );
-    if ( _overlayProxyContainer.valid() )
-        _overlayProxyContainer->replaceChild( oldChild, newChild );
-    return ok;
+    //nop
 }
 
-void
-DrapeableNode::traverse( osg::NodeVisitor& nv )
-{
-    if ( !_overlayProxyContainer.valid() )
-    {
-        osg::Group::traverse( nv );
-    }
-    else
-    {
-        if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
-        {
-            if ( _draped )
-            {
-                // do nothing -- culling will happen via the OverlayProxy instead.
-            }
-            else
-            {
-                // for a non-draped node, just traverse children as usual.
-                osg::Group::traverse( nv );
-            }
-        }
-
-        else if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR )
-        {
-            if ( _dirty )
-            {
-                applyChanges();
-                _dirty = false;
-                ADJUST_UPDATE_TRAV_COUNT( this, -1 );
-            }
-            
-            // traverse children directly, regardles of draped status
-            osg::Group::traverse( nv );
-        }
-
-        // handle other visitor types (like intersections, etc) by simply
-        // traversing the child graph.
-        else // if ( nv.getNodeVisitor() == osg::NodeVisitor::NODE_VISITOR )
-        {
-            osg::Group::traverse( nv );
-        }
-    }
-}
diff --git a/src/osgEarth/DrapingTechnique b/src/osgEarth/DrapingTechnique
new file mode 100644
index 0000000..1634561
--- /dev/null
+++ b/src/osgEarth/DrapingTechnique
@@ -0,0 +1,133 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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_OVERLAY_DRAPING_TECHNIQUE
+#define OSGEARTH_OVERLAY_DRAPING_TECHNIQUE
+
+#include <osgEarth/Common>
+#include <osgEarth/OverlayDecorator>
+#include <osg/TexGenNode>
+#include <osg/Uniform>
+
+namespace osgEarth
+{
+    class TerrainEngineNode;
+
+    /**
+     * Projects an overlay scene graph onto the main model.
+     *
+     * The OverlayDecorator is automatically installed in the MapNode and is used
+     * to display ModelLayer's whose "overlay" property is set to true.
+     *
+     * This class is similar in scope to osgSim::OverlayNode, but is optimized
+     * for use with osgEarth and geocentric terrains.
+     */
+    class OSGEARTH_EXPORT DrapingTechnique : public OverlayTechnique
+    {
+    public:
+        DrapingTechnique();
+
+        /**
+         * Explicity sets the texture unit to use. Note! When you add this class
+         * to a MapNode, it will automatically allocate a free texture unit; so you
+         * usually do NOT need to call this method.
+         */
+        void setTextureUnit( int unit );
+        int getTextureUnit() const { return *_textureUnit; }
+
+        /**
+         * The size (resolution in both directions) of the overlay texture. By
+         * default, this defaults to 4096 or your hardware's maximum supported
+         * texture size, whichever is less.
+         */
+        void setTextureSize( int texSize );
+        int getTextureSize() const { return *_textureSize; }
+
+        /**
+         * Whether mipmapping is enabled on the projected overlay texture. Mapmapping
+         * will improve the visual appearance, but will use more memory, and will affect
+         * performance for overlays that are dynamic. Mipmapping can slow things down
+         * a LOT on some GPUs (e.g. Intel GMA)
+         */
+        void setMipMapping( bool value );
+        bool getMipMapping() const { return _mipmapping; }
+
+        /**
+         * Whether to enable blending on the RTT camera graph. Default = true. You might
+         * want to disable this is you are draping polygons that cover a very large area
+         * and curve around the globe; sometimes they suffer from tessellation artifacts
+         * when draped.
+         */
+        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;
+
+
+    public: // OverlayTechnique
+
+        virtual bool hasData(
+            OverlayDecorator::TechRTTParams& params) const;
+
+        void reestablish(
+            TerrainEngineNode* engine );
+
+        void preCullTerrain(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv );
+
+        void cullOverlayGroup(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv );
+
+        void onInstall( TerrainEngineNode* engine );
+
+        void onUninstall( TerrainEngineNode* engine );
+
+
+    protected:
+        virtual ~DrapingTechnique() { }
+
+    private:
+        optional<int>                 _explicitTextureUnit;
+        optional<int>                 _textureUnit;
+        optional<int>                 _textureSize;
+        bool                          _useShaders;
+        bool                          _mipmapping;
+        bool                          _rttBlending;
+        bool                          _attachStencil;
+
+        struct TechData : public osg::Referenced
+        {
+            osg::ref_ptr<osg::Uniform>    _texGenUniform;
+            osg::ref_ptr<osg::TexGen>     _texGen;
+        };
+
+    private:
+        
+        void setUpCamera(OverlayDecorator::TechRTTParams& params);
+    };
+
+} // namespace osgEarth
+
+#endif //OSGEARTH_OVERLAY_DRAPING_TECHNIQUE
diff --git a/src/osgEarth/DrapingTechnique.cpp b/src/osgEarth/DrapingTechnique.cpp
new file mode 100644
index 0000000..a5d2cb7
--- /dev/null
+++ b/src/osgEarth/DrapingTechnique.cpp
@@ -0,0 +1,412 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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/DrapingTechnique>
+#include <osgEarth/Capabilities>
+#include <osgEarth/Registry>
+#include <osgEarth/VirtualProgram>
+
+#include <osg/BlendFunc>
+#include <osg/TexGen>
+#include <osg/Texture2D>
+#include <osg/Uniform>
+
+#define LC "[DrapingTechnique] "
+
+//#define OE_TEST if (_dumpRequested) OE_INFO << std::setprecision(9)
+#define OE_TEST OE_NULL
+
+using namespace osgEarth;
+
+//---------------------------------------------------------------------------
+
+namespace
+{
+    // Additional per-view data stored by the draping technique.
+    struct LocalPerViewData : public osg::Referenced
+    {
+        osg::ref_ptr<osg::Uniform> _texGenUniform;  // when shady
+        osg::ref_ptr<osg::TexGen>  _texGen;         // when not shady
+    };
+}
+
+//---------------------------------------------------------------------------
+
+DrapingTechnique::DrapingTechnique() :
+_textureUnit     ( 1 ),
+_textureSize     ( 1024 ),
+_useShaders      ( false ),
+_mipmapping      ( false ),
+_rttBlending     ( true ),
+_attachStencil   ( true )
+{
+    // nop
+}
+
+
+bool
+DrapingTechnique::hasData(OverlayDecorator::TechRTTParams& params) const
+{
+    return params._group->getNumChildren() > 0;
+}
+
+
+void
+DrapingTechnique::reestablish(TerrainEngineNode* engine)
+{
+    if ( !_textureUnit.isSet() )
+    {
+        // apply the user-request texture unit, if applicable:
+        if ( _explicitTextureUnit.isSet() )
+        {
+            if ( !_textureUnit.isSet() || *_textureUnit != *_explicitTextureUnit )
+            {
+                _textureUnit = *_explicitTextureUnit;
+            }
+        }
+
+        // otherwise, automatically allocate a texture unit if necessary:
+        else if ( !_textureUnit.isSet() )
+        {
+            int texUnit;
+            if ( engine->getTextureCompositor()->reserveTextureImageUnit( texUnit ) )
+            {
+                _textureUnit = texUnit;
+                OE_INFO << LC << "Reserved texture image unit " << *_textureUnit << std::endl;
+            }
+            else
+            {
+                OE_WARN << LC << "Uh oh, no texture image units available." << std::endl;
+            }
+        }
+    }
+}
+
+
+void
+DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
+{
+    // create the projected texture:
+    osg::Texture2D* projTexture = new osg::Texture2D();
+    projTexture->setTextureSize( *_textureSize, *_textureSize );
+    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_EDGE );
+    projTexture->setBorderColor( osg::Vec4(0,0,0,0) );
+
+    // set up the RTT camera:
+    params._rttCamera = new osg::Camera();
+    params._rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
+
+    // this ref frame causes the RTT to inherit its viewpoint from above (in order to properly
+    // process PagedLOD's etc. -- it doesn't affect the perspective of the RTT camera though)
+    params._rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
+    params._rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize );
+    params._rttCamera->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
+    params._rttCamera->setRenderOrder( osg::Camera::PRE_RENDER );
+    params._rttCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
+    params._rttCamera->attach( osg::Camera::COLOR_BUFFER, projTexture, 0, 0, _mipmapping );
+
+    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::capabilities().supportsDepthPackedStencilBuffer() )
+        {
+#ifdef OSG_GLES2_AVAILABLE 
+            params._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH24_STENCIL8_EXT );
+#else
+            params._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH_STENCIL_EXT );
+#endif
+        }
+        else
+        {
+            params._rttCamera->attach( osg::Camera::STENCIL_BUFFER, GL_STENCIL_INDEX );
+        }
+
+        params._rttCamera->setClearStencil( 0 );
+        params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+    }
+    else
+    {
+        params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+    }
+
+    // set up a StateSet for the RTT camera.
+    osg::StateSet* rttStateSet = params._rttCamera->getOrCreateStateSet();
+
+    // lighting is off. We don't want draped items to be lit.
+    rttStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
+
+    // install a new default shader program that replaces anything from above.
+    if ( _useShaders )
+    {
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setName( "DrapingTechnique RTT" );
+        vp->setInheritShaders( false );
+        rttStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+    }
+    
+    // active blending within the RTT camera's FBO
+    if ( _rttBlending )
+    {
+        //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;        
+        if (Registry::capabilities().supportsGLSL(1.4f))
+        {
+            //Blend Func Separate is only available on OpenGL 1.4 and above
+            blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+        }
+        else
+        {
+            blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        }
+
+        rttStateSet->setAttributeAndModes(blendFunc, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
+    }
+    else
+    {
+        rttStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
+    }
+
+    // attach the overlay group to the camera. 
+    // TODO: we should probably lock this since other cull traversals might be accessing the group
+    //       while we are changing its children.
+    params._rttCamera->addChild( params._group );
+
+    // overlay geometry is rendered with no depth testing, and in the order it's found in the
+    // scene graph... until further notice.
+    rttStateSet->setMode(GL_DEPTH_TEST, 0);
+    rttStateSet->setBinName( "TraversalOrderBin" );
+
+    // add to the terrain stateset, i.e. the stateset that the OverlayDecorator will
+    // apply to the terrain before cull-traversing it. This will activate the projective
+    // texturing on the terrain.
+    params._terrainStateSet->setTextureAttributeAndModes( *_textureUnit, projTexture, osg::StateAttribute::ON );
+
+    // fire up the local per-view data:
+    LocalPerViewData* local = new LocalPerViewData();
+    params._techniqueData = local;
+    
+    if ( _useShaders )
+    {            
+        // GPU path
+
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setName( "DrapingTechnique terrain shaders");
+        params._terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+
+        // sampler for projected texture:
+        params._terrainStateSet->getOrCreateUniform(
+            "oe_overlay_tex", osg::Uniform::SAMPLER_2D )->set( *_textureUnit );
+
+        // the texture projection matrix uniform.
+        local->_texGenUniform = params._terrainStateSet->getOrCreateUniform(
+            "oe_overlay_texmatrix", osg::Uniform::FLOAT_MAT4 );
+
+        // vertex shader - subgraph
+        std::string vs =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "uniform mat4 oe_overlay_texmatrix; \n"
+            "varying vec4 oe_overlay_texcoord; \n"
+
+            "void oe_overlay_vertex(inout vec4 VertexVIEW) \n"
+            "{ \n"
+            "    oe_overlay_texcoord = oe_overlay_texmatrix * VertexVIEW; \n"
+            "} \n";
+
+        vp->setFunction( "oe_overlay_vertex", vs, ShaderComp::LOCATION_VERTEX_VIEW );
+
+        // fragment shader - subgraph
+        std::string fs =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "uniform sampler2D oe_overlay_tex; \n"
+            "varying vec4      oe_overlay_texcoord; \n"
+
+            "void oe_overlay_fragment( inout vec4 color ) \n"
+            "{ \n"
+            "    vec4 texel = texture2DProj(oe_overlay_tex, oe_overlay_texcoord); \n"
+            "    color = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a); \n"
+            "} \n";
+
+        vp->setFunction( "oe_overlay_fragment", fs, ShaderComp::LOCATION_FRAGMENT_COLORING );
+    }
+    else
+    {
+        // FFP path
+        local->_texGen = new osg::TexGen();
+        local->_texGen->setMode( osg::TexGen::EYE_LINEAR );
+        params._terrainStateSet->setTextureAttributeAndModes( *_textureUnit, local->_texGen.get(), 1 );
+
+        osg::TexEnv* env = new osg::TexEnv();
+        env->setMode( osg::TexEnv::DECAL );
+        params._terrainStateSet->setTextureAttributeAndModes( *_textureUnit, env, 1 );
+    }
+}
+
+
+void
+DrapingTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
+                                 osgUtil::CullVisitor*             cv )
+{
+    if ( !params._rttCamera.valid() && params._group->getNumChildren() > 0 && _textureUnit.isSet() )
+    {
+        setUpCamera( params );
+    }
+
+    if ( params._rttCamera.valid() )
+    {
+        LocalPerViewData& local = *static_cast<LocalPerViewData*>(params._techniqueData.get());
+        if ( local._texGen.valid() )
+        {
+            // FFP path only
+            cv->getCurrentRenderBin()->getStage()->addPositionedTextureAttribute(
+                *_textureUnit, cv->getModelViewMatrix(), local._texGen.get() );
+        }
+    }
+}
+
+
+void
+DrapingTechnique::cullOverlayGroup(OverlayDecorator::TechRTTParams& params,
+                                   osgUtil::CullVisitor*            cv )
+{
+    if ( params._rttCamera.valid() )
+    {
+        // this xforms from clip [-1..1] to texture [0..1] space
+        static osg::Matrix s_scaleBiasMat = 
+            osg::Matrix::translate(1.0,1.0,1.0) * 
+            osg::Matrix::scale(0.5,0.5,0.5);
+
+        params._rttCamera->setViewMatrix      ( params._rttViewMatrix );
+        params._rttCamera->setProjectionMatrix( params._rttProjMatrix );
+
+        osg::Matrix VPT = params._rttViewMatrix * params._rttProjMatrix * s_scaleBiasMat;
+
+        LocalPerViewData& local = *static_cast<LocalPerViewData*>(params._techniqueData.get());
+
+        if ( local._texGenUniform.valid() )
+        {
+            // premultiply the inv view matrix so we don't have
+            // precision problems in the shader (and it's faster too)
+            local._texGenUniform->set( cv->getCurrentCamera()->getInverseViewMatrix() * VPT );
+        }
+        else
+        {
+            // FFP path
+            local._texGen->setPlanesFromMatrix( VPT );
+        }
+
+        // traverse the overlay group (via the RTT camera).
+        params._rttCamera->accept( *cv );
+    }
+}
+
+
+void
+DrapingTechnique::setTextureSize( int texSize )
+{
+    if ( texSize != _textureSize.value() )
+    {
+        _textureSize = texSize;
+    }
+}
+
+void
+DrapingTechnique::setTextureUnit( int texUnit )
+{
+    if ( !_explicitTextureUnit.isSet() || texUnit != _explicitTextureUnit.value() )
+    {
+        _explicitTextureUnit = texUnit;
+    }
+}
+
+void
+DrapingTechnique::setMipMapping( bool value )
+{
+    if ( value != _mipmapping )
+    {
+        _mipmapping = value;
+
+        if ( _mipmapping )
+            OE_INFO << LC << "Overlay mipmapping " << (value?"enabled":"disabled") << std::endl;
+    }
+}
+
+void
+DrapingTechnique::setOverlayBlending( bool value )
+{
+    if ( value != _rttBlending )
+    {
+        _rttBlending = value;
+        
+        if ( _rttBlending )
+            OE_INFO << LC << "Overlay blending " << (value?"enabled":"disabled")<< std::endl;
+    }
+}
+
+bool
+DrapingTechnique::getAttachStencil() const
+{
+    return _attachStencil;
+}
+
+void
+DrapingTechnique::setAttachStencil( bool value )
+{
+    _attachStencil = value;
+}
+
+void
+DrapingTechnique::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 = 
+        Registry::capabilities().supportsGLSL() && (
+            !engine->getTextureCompositor() ||
+            engine->getTextureCompositor()->usesShaderComposition() );
+
+    if ( !_textureSize.isSet() )
+    {
+        unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
+        _textureSize.init( osg::minimum( 4096u, maxSize ) );
+
+        OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
+    }
+}
+
+void
+DrapingTechnique::onUninstall( TerrainEngineNode* engine )
+{
+    if ( !_explicitTextureUnit.isSet() && _textureUnit.isSet() )
+    {
+        engine->getTextureCompositor()->releaseTextureImageUnit( *_textureUnit );
+        _textureUnit.unset();
+    }
+}
diff --git a/src/osgEarth/DrawInstanced b/src/osgEarth/DrawInstanced
index 08b60da..6623f0f 100644
--- a/src/osgEarth/DrawInstanced
+++ b/src/osgEarth/DrawInstanced
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/DrawInstanced.cpp b/src/osgEarth/DrawInstanced.cpp
index 2cf5b9f..c230f90 100644
--- a/src/osgEarth/DrawInstanced.cpp
+++ b/src/osgEarth/DrawInstanced.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -125,9 +125,9 @@ DrawInstanced::createDrawInstancedProgram()
         buf << "uniform mat4 oe_di_modelMatrix[" << MAX_COUNT_ARRAY << "];\n";
     }
 
-    buf << "void oe_di_setPosition()\n"
+    buf << "void oe_di_setPosition(inout vec4 VertexModel)\n"
         << "{\n"
-        << "    gl_Position = gl_ModelViewProjectionMatrix * oe_di_modelMatrix[gl_InstanceID] * gl_Vertex; \n"
+        << "    VertexModel = oe_di_modelMatrix[gl_InstanceID] * VertexModel; \n"
         << "}\n";
 
     std::string src;
@@ -136,7 +136,7 @@ DrawInstanced::createDrawInstancedProgram()
     vp->setFunction(
         "oe_di_setPosition",
         src,
-        ShaderComp::LOCATION_VERTEX_PRE_COLORING );
+        ShaderComp::LOCATION_VERTEX_MODEL );
 
     return vp;
 }
diff --git a/src/osgEarth/ECEF b/src/osgEarth/ECEF
index 6e0529d..fc5813c 100644
--- a/src/osgEarth/ECEF
+++ b/src/osgEarth/ECEF
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -31,6 +31,7 @@ namespace osgEarth
         /**
          * Creates a "localization" matrix for double-precision geocentric
          * coordinates. The matrix is ceneterd at the specified ECEF reference point.
+         * @deprecated - use SpatialReference::createLocalToWorld
          */
         static osg::Matrixd createLocalToWorld( 
             const osg::Vec3d& ecefRefPoint );
@@ -43,6 +44,7 @@ namespace osgEarth
             const osg::Vec3d&       input,
             const SpatialReference* inputSRS,
             osg::Vec3d&             output,
+            const SpatialReference* outputSRS,
             const osg::Matrixd&     world2local =osg::Matrixd() );
 
         /**
@@ -53,6 +55,21 @@ namespace osgEarth
             const std::vector<osg::Vec3d>& input,
             const SpatialReference*        inputSRS,
             osg::Vec3Array*                output,
+            const SpatialReference*        outputSRS,
+            const osg::Matrixd&            world2local =osg::Matrixd() );
+
+        /**
+         * Transforms the points in "input" to ECEF coordinates, localizes them with
+         * the provided world2local matrix, and puts the resulting verts in "out_verts"
+         * and the resulting localized normals in "out_normals" (if "out_normals" is
+         * non-NULL).
+         */
+        static void transformAndLocalize(
+            const std::vector<osg::Vec3d>& input,
+            const SpatialReference*        inputSRS,
+            osg::Vec3Array*                out_verts,
+            osg::Vec3Array*                out_normals,
+            const SpatialReference*        outputSRS,
             const osg::Matrixd&            world2local =osg::Matrixd() );
 
         /**
@@ -61,10 +78,10 @@ namespace osgEarth
          */
         static void transformAndGetRotationMatrix(
             const osg::Vec3d&       input,
-            const SpatialReference* input_srs,
+            const SpatialReference* inputSRS,
             osg::Vec3d&             out_ecef_point,
+            const SpatialReference* outputSRS,
             osg::Matrixd&           out_rotation );
-
     };
 }
 
diff --git a/src/osgEarth/ECEF.cpp b/src/osgEarth/ECEF.cpp
index d951c0a..6c74a6c 100644
--- a/src/osgEarth/ECEF.cpp
+++ b/src/osgEarth/ECEF.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
  */
 
 #include <osgEarth/ECEF>
+#include <osgEarth/Notify>
 
 using namespace osgEarth;
 
@@ -25,7 +26,6 @@ using namespace osgEarth;
 
 // --------------------------------------------------------------------------
 
-
 osg::Matrixd
 ECEF::createLocalToWorld( const osg::Vec3d& input )
 {
@@ -70,10 +70,12 @@ void
 ECEF::transformAndLocalize(const osg::Vec3d&       input,
                            const SpatialReference* inputSRS,
                            osg::Vec3d&             output,
+                           const SpatialReference* outputSRS,
                            const osg::Matrixd&     world2local)
 {
     osg::Vec3d ecef;
-    inputSRS->transformToECEF( input, ecef );
+    //inputSRS->transformToECEF( input, ecef );
+    inputSRS->transform( input, outputSRS->getECEF(), ecef );
     output = ecef * world2local;
 }
 
@@ -82,39 +84,73 @@ void
 ECEF::transformAndLocalize(const std::vector<osg::Vec3d>& input,
                            const SpatialReference*        inputSRS,
                            osg::Vec3Array*                output,
+                           const SpatialReference*        outputSRS,
                            const osg::Matrixd&            world2local )
 {
+    const SpatialReference* ecefSRS = outputSRS->getECEF();
     output->reserve( output->size() + input.size() );
+
     for( std::vector<osg::Vec3d>::const_iterator i = input.begin(); i != input.end(); ++i )
     {
         osg::Vec3d ecef;
-        inputSRS->transformToECEF( *i, ecef );
+        inputSRS->transform( *i, ecefSRS, ecef );
+        //inputSRS->transformToECEF( *i, ecef );
         output->push_back( ecef * world2local );
     }
 }
 
+
+void
+ECEF::transformAndLocalize(const std::vector<osg::Vec3d>& input,
+                           const SpatialReference*        inputSRS,
+                           osg::Vec3Array*                out_verts,
+                           osg::Vec3Array*                out_normals,
+                           const SpatialReference*        outputSRS,
+                           const osg::Matrixd&            world2local )
+{
+    const SpatialReference* ecefSRS = outputSRS->getECEF();
+    out_verts->reserve( out_verts->size() + input.size() );
+
+    if ( out_normals )
+        out_normals->reserve( out_verts->size() );
+
+    for( std::vector<osg::Vec3d>::const_iterator i = input.begin(); i != input.end(); ++i )
+    {
+        osg::Vec3d ecef;
+        inputSRS->transform( *i, ecefSRS, ecef );
+        out_verts->push_back( ecef * world2local );
+
+        if ( out_normals )
+        {
+            ecef.normalize();
+            out_normals->push_back( osg::Matrix::transform3x3(ecef, world2local) );
+        }
+    }
+}
+
 void
 ECEF::transformAndGetRotationMatrix(const osg::Vec3d&       input,
                                     const SpatialReference* inputSRS,
                                     osg::Vec3d&             out_point,
+                                    const SpatialReference* outputSRS,
                                     osg::Matrixd&           out_rotation )
 {
-    osg::Vec3d geod_point;
+    const SpatialReference* geoSRS  = inputSRS->getGeographicSRS();
+    const SpatialReference* ecefSRS = outputSRS->getECEF();
+
+    // first transform the geographic (lat/long):
+    osg::Vec3d geoPoint;
     if ( !inputSRS->isGeographic() )
-        inputSRS->transform( input, inputSRS->getGeographicSRS(), geod_point );
+        inputSRS->transform( input, geoSRS, geoPoint );
     else
-        geod_point = input;
-
-    const osg::EllipsoidModel* em = inputSRS->getEllipsoid();
-    
-    em->convertLatLongHeightToXYZ(
-        osg::DegreesToRadians( geod_point.y() ),
-        osg::DegreesToRadians( geod_point.x() ),
-        geod_point.z(),
-        out_point.x(), out_point.y(), out_point.z() );
-
-    em->computeCoordinateFrame(
-        osg::DegreesToRadians( geod_point.y() ),
-        osg::DegreesToRadians( geod_point.x() ),
+        geoPoint = input;
+
+    // use that information to calculate a rotation matrix:
+    ecefSRS->getEllipsoid()->computeCoordinateFrame(
+        osg::DegreesToRadians( geoPoint.y() ),
+        osg::DegreesToRadians( geoPoint.x() ),
         out_rotation );
+
+    // then convert that to ECEF.
+    geoSRS->transform(geoPoint, ecefSRS, out_point);
 }
diff --git a/src/osgEarth/ElevationLOD b/src/osgEarth/ElevationLOD
index a648936..d79b7dc 100644
--- a/src/osgEarth/ElevationLOD
+++ b/src/osgEarth/ElevationLOD
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -27,16 +27,20 @@
 namespace osgEarth
 {
     /**
-     * Decorator node that will only display it's children when the camera is within a given elevation range
+     * Decorator node that will only display it's children when the
+     * camera is within a given elevation range and/or within a
+     * given camera range.
      */
     class OSGEARTH_EXPORT ElevationLOD : public osg::Group
     {
     public:
+        META_Node(osgEarth, ElevationLOD);
+
+        ElevationLOD();
+        ElevationLOD(const ElevationLOD& rhs, const osg::CopyOp& op);
         ElevationLOD(const SpatialReference* srs);
         ElevationLOD(const SpatialReference* srs, double minElevation, double maxElevation);
 
-        virtual ~ElevationLOD();
-
         double getMinElevation() const;
         void setMinElevation( double minElevation );
 
@@ -45,13 +49,29 @@ namespace osgEarth
 
         void setElevations( double minElevation, double maxElevation );
 
-        virtual void traverse( osg::NodeVisitor& nv);       
-               
+        float getMinRange() const;
+        void setMinRange(float minRange);
+
+        float getMaxRange() const;
+        void setMaxRange(float maxRange);
+
+        void setRanges( float minRange, float maxRange );
+
+
+    public: // osg::Node
+
+        virtual void traverse( osg::NodeVisitor& nv);
+
+    protected:
+
+        virtual ~ElevationLOD();
 
     private:
         osg::ref_ptr< const SpatialReference > _srs;
-        double _minElevation;
-        double _maxElevation;
+        optional<double> _minElevation;
+        optional<double> _maxElevation;
+        optional<float>  _minRange;
+        optional<float>  _maxRange;
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/ElevationLOD.cpp b/src/osgEarth/ElevationLOD.cpp
index b6a4913..c679c82 100644
--- a/src/osgEarth/ElevationLOD.cpp
+++ b/src/osgEarth/ElevationLOD.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,36 +17,59 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarth/ElevationLOD>
+#include <osgEarth/CullingUtils>
 #include <osgEarth/GeoData>
-
-#include <osgUtil/CullVisitor>
 #include <osg/CoordinateSystemNode>
 
 using namespace osgEarth;
 
 
+ElevationLOD::ElevationLOD() :
+_minElevation( -DBL_MAX ),
+_maxElevation( DBL_MAX ),
+_minRange    ( 0.0f ),
+_maxRange    ( FLT_MAX )
+{
+    //nop
+}
+
+ElevationLOD::ElevationLOD( const ElevationLOD& rhs, const osg::CopyOp& op) :
+_minElevation( rhs._minElevation ),
+_maxElevation( rhs._maxElevation ),
+_minRange    ( rhs._minRange ),
+_maxRange    ( rhs._maxRange ),
+_srs         ( rhs._srs.get() )
+{
+    //nop
+}
 
 ElevationLOD::ElevationLOD(const SpatialReference* srs):
-_minElevation(-DBL_MAX),
-_maxElevation(DBL_MAX),
-_srs( srs )
+_minElevation( -DBL_MAX ),
+_maxElevation( DBL_MAX ),
+_minRange    ( 0.0f ),
+_maxRange    ( FLT_MAX ),
+_srs         ( srs )
 {
+    //nop
 }
 
 ElevationLOD::ElevationLOD(const SpatialReference* srs, double minElevation, double maxElevation):
-_minElevation( minElevation ),
-_maxElevation( maxElevation ),
-_srs( srs )
+_minRange    ( 0.0f ),
+_maxRange    ( FLT_MAX ),
+_srs         ( srs )
 {
+    _minElevation = minElevation;
+    _maxElevation = maxElevation;
 }
 
 ElevationLOD::~ElevationLOD()
 {
+    //nop
 }
 
 double ElevationLOD::getMinElevation() const
 {
-    return _minElevation;
+    return *_minElevation;
 }
         
 void ElevationLOD::setMinElevation( double minElevation )
@@ -56,7 +79,7 @@ void ElevationLOD::setMinElevation( double minElevation )
 
 double ElevationLOD::getMaxElevation() const
 {
-    return _maxElevation;
+    return *_maxElevation;
 }
 
 void ElevationLOD::setMaxElevation(double maxElevation )
@@ -70,31 +93,86 @@ void ElevationLOD::setElevations( double minElevation, double maxElevation )
     _maxElevation = maxElevation;
 }
 
+void ElevationLOD::setMinRange(float value)
+{
+    _minRange = value;
+}
+
+float ElevationLOD::getMinRange() const
+{
+    return *_minRange;
+}
+
+void ElevationLOD::setMaxRange(float value)
+{
+    _maxRange = value;
+}
+
+float ElevationLOD::getMaxRange() const
+{
+    return *_maxRange;
+}
+
 void ElevationLOD::traverse( osg::NodeVisitor& nv)
 {
-    if (nv.getVisitorType() ==  osg::NodeVisitor::CULL_VISITOR)
+    if (nv.getVisitorType()   == osg::NodeVisitor::CULL_VISITOR &&
+        nv.getTraversalMode() == osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN )
     {
-        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( &nv );
-        osg::Vec3d eye, center, up;        
-        eye = cv->getViewPoint();
+        bool rangeOK     = true;
+        bool altitudeOK  = true;
 
-        float height = eye.z();
-        if (_srs)
+        // first test the range:
+        if ( _minRange.isSet() || _maxRange.isSet() )
         {
-            GeoPoint mapPoint;
-            mapPoint.fromWorld( _srs, eye );        
-            height = mapPoint.z();
+            float range = nv.getDistanceToViewPoint( getBound().center(), true );
+            rangeOK =
+                (!_minRange.isSet() || (range >= *_minRange)) &&
+                (!_maxRange.isSet() || (range <= *_maxRange));
         }
 
-        //OE_NOTICE << "Height " << height << std::endl;
-
-        if (height >= _minElevation && height <= _maxElevation)
+        if ( rangeOK )
         {
-            osg::Group::traverse( nv );
-        }
-        else
-        {
-            //OE_NOTICE << "Elevation " << height << " outside of range " << _minElevation << " to " << _maxElevation << std::endl;
+            if ( _minElevation.isSet() || _maxElevation.isSet() )
+            {
+                double alt;
+
+                // first see if we have a precalculated elevation:
+                osgUtil::CullVisitor*  cv = Culling::asCullVisitor(nv);
+                Culling::CullUserData* ud = Culling::getCullUserData(cv);
+                if ( ud && ud->_cameraAltitude.isSet() )
+                {
+                    // yes; use it
+                    alt = ud->_cameraAltitude.get();
+                }
+                else
+                {
+                    // no; need to calculate elevation here:
+                    osg::Vec3d eye = cv->getViewPoint();
+
+                    if ( _srs && !_srs->isProjected() )
+                    {
+                        GeoPoint mapPoint;
+                        mapPoint.fromWorld( _srs.get(), eye );
+                        alt = mapPoint.z();
+                    }
+                    else
+                    {
+                        alt = eye.z();
+                    }
+                }
+
+                // account for the LOD scale
+                alt *= cv->getLODScale();
+
+                altitudeOK =
+                    (!_minElevation.isSet() || (alt >= *_minElevation)) &&
+                    (!_maxElevation.isSet() || (alt <= *_maxElevation));
+            }
+
+            if ( altitudeOK )
+            {
+                std::for_each(_children.begin(),_children.end(),osg::NodeAcceptOp(nv));
+            }
         }
     }
     else
diff --git a/src/osgEarth/ElevationLayer b/src/osgEarth/ElevationLayer
index 8cf71fc..3793996 100644
--- a/src/osgEarth/ElevationLayer
+++ b/src/osgEarth/ElevationLayer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -163,6 +163,23 @@ namespace osgEarth
             osg::ref_ptr<osg::HeightField>& out_result,
             bool*                           out_isFallback,
             ProgressCallback*               progress ) const;
+
+    public:
+        /** Default ctor */
+        ElevationLayerVector();
+
+        /** Copy ctor */
+        ElevationLayerVector(const ElevationLayerVector& rhs);
+
+        /**
+         * Sets a tile size that createHeightField will always return.
+         * By default, it will return a heightfield whose tile size matches
+         * that of the largest tile size found in the source data layers.
+         */
+        void setExpressTileSize( unsigned tileSize );
+
+    private:
+        optional<unsigned> _expressTileSize;
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/ElevationLayer.cpp b/src/osgEarth/ElevationLayer.cpp
index e51dbfb..e5b14b3 100644
--- a/src/osgEarth/ElevationLayer.cpp
+++ b/src/osgEarth/ElevationLayer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -277,6 +277,10 @@ ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
     std::vector< TileKey > intersectingTiles;
     getProfile()->getIntersectingTiles( key, intersectingTiles );
 
+
+    //Maintain a list of heightfield tiles that have been added to the list already.
+    std::set< osgTerrain::TileID > existingTiles; 
+
     // collect heightfield for each intersecting key. Note, we're hitting the
     // underlying tile source here, so there's no vetical datum shifts happening yet.
     // we will do that later.
@@ -293,6 +297,33 @@ ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
                 {
                     heightFields.push_back( GeoHeightField(hf, layerKey.getExtent()) );
                 }
+                else
+                { 
+                    // We couldn't get a heightfield at the given key so fall back on parent tiles
+                    TileKey parentKey = layerKey.createParentKey();
+                    while (!hf && parentKey.valid())
+                    {
+                        // Make sure we haven't already added this heightfield to the list.
+                        // This could happen if you have multiple high resolution tiles that dont' have data.
+                        // So if you have four level 5 tiles with no data, they will fall back on the same level 4 tile.
+                        // This existingTiles check makes sure we don't process and add the same tile multiple times
+                        if (existingTiles.find(parentKey.getTileId()) == existingTiles.end()) 
+                        {
+                            hf = createHeightFieldFromTileSource( parentKey, progress );
+                            if (hf)
+                            {
+                                heightFields.push_back( GeoHeightField(hf, parentKey.getExtent()) );                                
+                                existingTiles.insert(parentKey.getTileId());
+                                break;
+                            }                        
+                            parentKey = parentKey.createParentKey();
+                        }                        
+                        else
+                        {                            
+                            break;
+                        }                        
+                    }                    
+                }
             }
         }
     }
@@ -308,9 +339,12 @@ ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
             if (itr->getHeightField()->getNumColumns() > width)
                 width = itr->getHeightField()->getNumColumns();
             if (itr->getHeightField()->getNumRows() > height) 
-                height = itr->getHeightField()->getNumRows();
+                height = itr->getHeightField()->getNumRows();                        
         }
 
+        //Now sort the heightfields by resolution to make sure we're sampling the highest resolution one first.
+        std::sort( heightFields.begin(), heightFields.end(), GeoHeightField::SortByResolutionFunctor());        
+
         result = new osg::HeightField();
         result->allocate(width, height);
 
@@ -443,6 +477,29 @@ ElevationLayer::createHeightField(const TileKey&    key,
 
 //------------------------------------------------------------------------
 
+#undef  LC
+#define LC "[ElevationLayers] "
+
+ElevationLayerVector::ElevationLayerVector()
+{
+    //nop
+}
+
+
+ElevationLayerVector::ElevationLayerVector(const ElevationLayerVector& rhs) :
+osg::MixinVector< osg::ref_ptr<ElevationLayer> >( rhs ),
+_expressTileSize( rhs._expressTileSize )
+{
+    //nop
+}
+
+
+void
+ElevationLayerVector::setExpressTileSize(unsigned tileSize)
+{
+    _expressTileSize = tileSize;
+}
+
 
 bool
 ElevationLayerVector::createHeightField(const TileKey&                  key,
@@ -453,7 +510,7 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
                                         osg::ref_ptr<osg::HeightField>& out_result,
                                         bool*                           out_isFallback,
                                         ProgressCallback*               progress )  const
-{        
+{
     unsigned lowestLOD = key.getLevelOfDetail();
     bool hfInitialized = false;
 
@@ -481,12 +538,10 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
 
     // 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() )
+        if ( layer->getVisible() && layer->isKeyValid( keyToUse ) )
         {
             GeoHeightField geoHF = layer->createHeightField( keyToUse, progress );
 
@@ -505,7 +560,9 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
                 if ( geoHF.valid() )
                 {
                     if ( hf_key.getLevelOfDetail() < lowestLOD )
+                    {
                         lowestLOD = hf_key.getLevelOfDetail();
+                    }
 
                     //This HeightField is fallback data, so increment the count.
                     numFallbacks++;
@@ -531,7 +588,13 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
         //If we got no heightfields but were requested to fallback, create an empty heightfield.
         if ( fallback )
         {
-            out_result = HeightFieldUtils::createReferenceHeightField( keyToUse.getExtent(), defElevSize, defElevSize );                
+            unsigned defaultSize = _expressTileSize.getOrUse( 8 );
+
+            out_result = HeightFieldUtils::createReferenceHeightField( 
+                keyToUse.getExtent(), 
+                defaultSize, 
+                defaultSize );
+
             return true;
         }
         else
@@ -545,7 +608,7 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
     {
         if ( lowestLOD == key.getLevelOfDetail() )
         {
-            //If we only have on heightfield, just return it.
+            // If we only have on heightfield, just return it.
             out_result = heightFields[0].takeHeightField();
         }
         else
@@ -554,25 +617,47 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
             out_result = geoHF.takeHeightField();
             hfInitialized = true;
         }
+
+        // resample if necessary:
+        if ( _expressTileSize.isSet() )
+        {
+            out_result = HeightFieldUtils::resampleHeightField(
+                out_result.get(),
+                *_expressTileSize,
+                *_expressTileSize,
+                interpolation );
+        }
     }
 
     else
     {
-        //If we have multiple heightfields, we need to composite them together.
+        // 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 ( _expressTileSize.isSet() )
         {
-            if (i->getHeightField()->getNumColumns() > width) 
-                width = i->getHeightField()->getNumColumns();
-            if (i->getHeightField()->getNumRows() > height) 
-                height = i->getHeightField()->getNumRows();
+            // user set a tile size; use it.
+            width  = *_expressTileSize;
+            height = *_expressTileSize;
         }
+        else
+        {
+            // user did not ask for a tile size; find the biggest among the layers.
+            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();
+            }
+        }
+
+        // make the new heightfield.
         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
+        // calculate the post spacings.
         double minx, miny, maxx, maxy;
         key.getExtent().getBounds(minx, miny, maxx, maxy);
         double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
@@ -580,7 +665,7 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
 
         const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();
 
-        //Create the new heightfield by sampling all of them.
+        // Create the new heightfield by sampling all layer heightfields.
         for (unsigned int c = 0; c < width; ++c)
         {
             double x = minx + (dx * (double)c);
@@ -662,13 +747,9 @@ ElevationLayerVector::createHeightField(const TileKey&                  key,
             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
+    // Initialize the HF values
     if (out_result.valid() && !hfInitialized )
     {   
         //Go ahead and set up the heightfield so we don't have to worry about it later
diff --git a/src/osgEarth/ElevationQuery b/src/osgEarth/ElevationQuery
index c4082d4..4329d74 100644
--- a/src/osgEarth/ElevationQuery
+++ b/src/osgEarth/ElevationQuery
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -32,16 +32,6 @@ namespace osgEarth
      * give it the DEM resolution at which you want an elevation point, and it will
      * access the necessary tile and sample it.
      *
-     * EQ supports two types of sampling:
-     *
-     * PARAMTERIC - EQ will sample the actual heightfield directly. This method is the
-     *   fastest since it does not require geometry intersection testing.
-     *
-     * GEOMETRIC - EQ will create a temporary tesselated terrain tile and do an 
-     *   intersection test (using osgUtil::IntersectionVisitor). This method is slower
-     *   but more visually correlated. (TODO: not really true anymore. This method
-     *   will require a re-do in osgEarth 2.2 or 3.0)
-     *
      * NOTE: EQ does NOT take into account rendering properties like vertical scale or
      * skirts. If you need a vertical scale, for example, simply scale the resulting
      * elevation value.
@@ -50,16 +40,19 @@ namespace osgEarth
     {
     public:
         /**
-         * Constructs a new elevation manager. If you are not using a MapNode,
-         * use this constructor to perform elevation queries against a Map. If
-         * you *do* have a MapNode, use the other CTOR that takes a MapNode.
+         * Constructs a new elevation manager.
          *
          * @param map
          *      Map against which to perform elevation queries.
-         * @param technique
-         *      Technique to use for elevation data sampling.
          */
         ElevationQuery( const Map* map );
+        
+        /**
+         * Constructs a new elevation manager.
+         *
+         * @param mapFrame
+         *      Map frame against which to perform elevation queries.
+         */
         ElevationQuery( const MapFrame& mapFrame );
 
         /** dtor */
@@ -133,13 +126,17 @@ namespace osgEarth
          */
         double getAverageQueryTime() const { return _queries > 0.0 ? _totalTime/_queries : 0.0; }
 
+        /**
+         * Gets the maximum level of data available at the given point.  If the layers have DataExtents provided they
+         * will be queried.  This allows certain areas on the earth to have higher levels of detail
+         * than others and avoids unnecessary queries where there isn't high resolution data available.
+         */
         unsigned int getMaxLevel(double x, double y, const SpatialReference* srs, const Profile* profile ) const;
 
     private:
         MapFrame  _mapf;
         unsigned  _maxCacheSize;
-        int       _tileSize;
-        unsigned  _maxDataLevel;
+        int       _tileSize;        
         int       _maxLevelOverride;
 
         typedef LRUCache< TileKey, osg::ref_ptr<osg::HeightField> > TileCache;
diff --git a/src/osgEarth/ElevationQuery.cpp b/src/osgEarth/ElevationQuery.cpp
index be1f7fa..e21dea5 100644
--- a/src/osgEarth/ElevationQuery.cpp
+++ b/src/osgEarth/ElevationQuery.cpp
@@ -25,8 +25,6 @@ void
 ElevationQuery::postCTOR()
 {
     _tileSize         = 0;
-    _maxDataLevel     = 0;
-    //_technique        = TECHNIQUE_PARAMETRIC;
     _maxLevelOverride = -1;
     _queries          = 0.0;
     _totalTime        = 0.0;
@@ -39,10 +37,9 @@ ElevationQuery::postCTOR()
 void
 ElevationQuery::sync()
 {
-    if ( _mapf.sync() || _tileSize == 0 || _maxDataLevel == 0 )
+    if ( _mapf.sync() || _tileSize == 0  )
     {
-        _tileSize = 0;
-        _maxDataLevel = 0;
+        _tileSize = 0;        
 
         for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i )
         {
@@ -50,11 +47,6 @@ ElevationQuery::sync()
             int layerTileSize = i->get()->getTileSize();
             if ( layerTileSize > _tileSize )
                 _tileSize = layerTileSize;
-
-            // we also need the maximum available data level.
-            unsigned int layerMaxDataLevel = i->get()->getMaxDataLevel();
-            if ( layerMaxDataLevel > _maxDataLevel )
-                _maxDataLevel = layerMaxDataLevel;
         }
     }
 }
@@ -86,12 +78,8 @@ ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, co
             }
 
             //Need to convert the layer max of this TileSource to that of the actual profile
-            layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax );
+            layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax );            
         }
-        else
-        {
-            layerMax = i->get()->getMaxDataLevel();
-        }        
 
         if ( i->get()->getTerrainLayerRuntimeOptions().maxLevel().isSet() )
             layerMax = std::min( layerMax, *i->get()->getTerrainLayerRuntimeOptions().maxLevel() );
@@ -124,20 +112,20 @@ ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, co
             }
 
             //Need to convert the layer max of this TileSource to that of the actual profile
-            layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax );
-        }
-        else
-        {
-            layerMax = i->get()->getMaxDataLevel();
-        }
-        
+            layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax );            
+        }        
         
         if ( i->get()->getTerrainLayerRuntimeOptions().maxLevel().isSet() )
             layerMax = std::min( layerMax, *i->get()->getTerrainLayerRuntimeOptions().maxLevel() );
 
         if (layerMax > maxLevel)
             maxLevel = layerMax;
-    }    
+    } 
+
+    if (maxLevel == 0) 
+    {
+        //This means we had no data extents on any of our layers and no max levels are set
+    }
 
     return maxLevel;
 }
@@ -227,14 +215,13 @@ ElevationQuery::getElevationImpl(const GeoPoint& point,
                                  double*         out_actualResolution)
 {
     osg::Timer_t start = osg::Timer::instance()->tick();
-
-    if ( _maxDataLevel == 0 || _tileSize == 0 )
+    
+    if ( _mapf.elevationLayers().empty() )
     {
         // this means there are no heightfields.
         out_elevation = 0.0;
         return true;
     }
-
     
     //This is the max resolution that we actually have data at this point
     unsigned int bestAvailLevel = getMaxLevel( point.x(), point.y(), point.getSRS(), _mapf.getProfile());
@@ -276,9 +263,11 @@ ElevationQuery::getElevationImpl(const GeoPoint& point,
     // fallback on a lower resolution, this cache will hold the final resolution heightfield
     // instead of trying to fetch the higher resolution one each item.
 
-    TileCache::Record record = _tileCache.get( key );
-    if ( record.valid() )
+    TileCache::Record record;
+    if ( _tileCache.get(key, record) )
+    {
         tile = record.value().get();
+    }
 
     // if we didn't find it, build it.
     if ( !tile.valid() )
@@ -313,7 +302,7 @@ ElevationQuery::getElevationImpl(const GeoPoint& point,
         tile.get(), 
         mapPoint.x(), mapPoint.y(), 
         extent.xMin(), extent.yMin(), 
-        xInterval, yInterval );
+        xInterval, yInterval, _mapf.getMapInfo().getElevationInterpolation() );
 
     osg::Timer_t end = osg::Timer::instance()->tick();
     _queries++;
diff --git a/src/osgEarth/Export b/src/osgEarth/Export
index 2fcc376..fb40055 100644
--- a/src/osgEarth/Export
+++ b/src/osgEarth/Export
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/FadeEffect b/src/osgEarth/FadeEffect
index 278db4b..1ddc137 100644
--- a/src/osgEarth/FadeEffect
+++ b/src/osgEarth/FadeEffect
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
 #define OSGEARTH_FADE_EFFECT_H 1
 
 #include <osgEarth/Common>
+#include <osgEarth/Config>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Group>
 #include <osg/Uniform>
@@ -28,6 +29,38 @@
 namespace osgEarth
 {
     /**
+     * Options for geometry fading properties.
+     */
+    class OSGEARTH_EXPORT FadeOptions
+    {
+    public:
+        /** Construct new default fade options */
+        FadeOptions(const Config& conf =Config());
+
+        /** Time to fade in a new graph (seconds) */
+        optional<float>& duration() { return _duration; }
+        const optional<float>& duration() const { return _duration; }
+
+        /** Range at which geometry becomes invisible (m) */
+        optional<float>& maxRange() { return _maxRange; }
+        const optional<float>& maxRange() const { return _maxRange; }
+
+        /** Distance over which to fade out geometry (from max range) */
+        optional<float>& attentuationDistance() { return _attenDist; }
+        const optional<float>& attenuationDistance() const { return _attenDist; }
+
+    public:
+        virtual ~FadeOptions() { }
+        Config getConfig() const;
+
+    private:
+        optional<float> _duration;
+        optional<float> _maxRange;
+        optional<float> _attenDist;
+    };
+
+
+    /**
      * Decorator node that will only display it's children when the camera is within a given elevation range
      */
     class OSGEARTH_EXPORT FadeEffect : public osg::Group
@@ -45,11 +78,22 @@ namespace osgEarth
         FadeEffect();
         virtual ~FadeEffect() { }
 
+        /** Time over which to fade in the subgraph */
         void setFadeDuration( float seconds );
         float getFadeDuration() const;
 
+        /** Camera range at which subgraph is completely faded out */
+        void setMaxRange( float range );
+        float getMaxRange() const;
+
+        /** Distance over which to fade out the subgraph */
+        void setAttenuationDistance( float dist );
+        float getAttenuationDistance() const;
+
     private:
         osg::ref_ptr<osg::Uniform> _fadeDuration;
+        osg::ref_ptr<osg::Uniform> _maxRange;
+        osg::ref_ptr<osg::Uniform> _attenDist;
     };
 
 
diff --git a/src/osgEarth/FadeEffect.cpp b/src/osgEarth/FadeEffect.cpp
index fae0b9e..9e179d0 100644
--- a/src/osgEarth/FadeEffect.cpp
+++ b/src/osgEarth/FadeEffect.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,12 +20,34 @@
 #include <osgEarth/VirtualProgram>
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
-#include <osgUtil/CullVisitor>
+#include <osgEarth/CullingUtils>
 
 using namespace osgEarth;
 
 //--------------------------------------------------------------------
 
+FadeOptions::FadeOptions(const Config& conf) :
+_duration      ( 1.0f ),
+_maxRange      ( FLT_MAX ),
+_attenDist     ( 1000.0f )
+{
+    conf.getIfSet( "duration",             _duration );
+    conf.getIfSet( "max_range",            _maxRange );
+    conf.getIfSet( "attenuation_distance", _attenDist );
+}
+
+Config
+FadeOptions::getConfig() const
+{
+    Config conf("fading");
+    conf.addIfSet( "duration",             _duration );
+    conf.addIfSet( "max_range",            _maxRange );
+    conf.addIfSet( "attenuation_distance", _attenDist );
+    return conf;
+}
+
+//--------------------------------------------------------------------
+
 namespace
 {
     char* FadeEffectVertexShader =
@@ -33,15 +55,19 @@ namespace
 #ifdef OSG_GLES2_AVAILABLE
         "precision mediump float; \n"
 #endif
-        "uniform float oe_FadeEffect_duration; \n"
-        "uniform float oe_FadeEffect_startTime; \n"
+        "uniform float oe_fadeeffect_duration; \n"
+        "uniform float oe_fadeeffect_startTime; \n"
+        "uniform float oe_fadeeffect_maxRange; \n"
+        "uniform float oe_fadeeffect_attenDist; \n"
         "uniform float osg_FrameTime; \n"
-        "varying float oe_FadeEffect_opacity; \n"
 
-        "void oe_vertFadeEffect() \n"
+        "varying float oe_fadeeffect_opacity; \n"
+
+        "void oe_vertFadeEffect(inout vec4 VertexView) \n"
         "{ \n"
-        "    float t = (osg_FrameTime-oe_FadeEffect_startTime)/oe_FadeEffect_duration; \n"
-        "    oe_FadeEffect_opacity = clamp( t, 0.0, 1.0 ); \n"
+        "    float t = (osg_FrameTime-oe_fadeeffect_startTime)/oe_fadeeffect_duration; \n"
+        "    float r = (oe_fadeeffect_maxRange - (-VertexView.z))/oe_fadeeffect_attenDist; \n"
+        "    oe_fadeeffect_opacity = clamp(t, 0.0, 1.0) * clamp(r, 0.0, 1.0); \n"
         "} \n";
 
     char* FadeEffectFragmentShader = 
@@ -49,11 +75,11 @@ namespace
 #ifdef OSG_GLES2_AVAILABLE
         "precision mediump float; \n"
 #endif
-        "varying float oe_FadeEffect_opacity; \n"
+        "varying float oe_fadeeffect_opacity; \n"
 
         "void oe_fragFadeEffect( inout vec4 color ) \n"
         "{ \n"
-        "    color.a *= oe_FadeEffect_opacity; \n"
+        "    color.a *= oe_fadeeffect_opacity; \n"
         "} \n";
 }
 
@@ -62,7 +88,7 @@ namespace
 osg::Uniform*
 FadeEffect::createStartTimeUniform()
 {
-    return new osg::Uniform( osg::Uniform::FLOAT, "oe_FadeEffect_startTime" );
+    return new osg::Uniform( osg::Uniform::FLOAT, "oe_fadeeffect_startTime" );
 }
 
 FadeEffect::FadeEffect()
@@ -73,15 +99,20 @@ FadeEffect::FadeEffect()
     {
         VirtualProgram* vp = new VirtualProgram();
 
-        vp->setFunction( "oe_vertFadeEffect", FadeEffectVertexShader,   ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
-        vp->setFunction( "oe_fragFadeEffect", FadeEffectFragmentShader, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+        vp->setFunction( "oe_vertFadeEffect", FadeEffectVertexShader,   ShaderComp::LOCATION_VERTEX_VIEW );
+        vp->setFunction( "oe_fragFadeEffect", FadeEffectFragmentShader, ShaderComp::LOCATION_FRAGMENT_COLORING );
 
         ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
-    }
 
-    _fadeDuration = new osg::Uniform( osg::Uniform::FLOAT, "oe_FadeEffect_duration" );
-    _fadeDuration->set( 1.0f );
-    ss->addUniform( _fadeDuration );
+        _fadeDuration = ss->getOrCreateUniform( "oe_fadeeffect_duration", osg::Uniform::FLOAT );
+        _fadeDuration->set( 1.0f );
+
+        _maxRange = ss->getOrCreateUniform( "oe_fadeeffect_maxRange", osg::Uniform::FLOAT );
+        _maxRange->set( FLT_MAX );
+
+        _attenDist = ss->getOrCreateUniform( "oe_fadeeffect_attenDist", osg::Uniform::FLOAT );
+        _attenDist->set( 0.0f );
+    }
 
     ss->setMode( GL_BLEND, 1 );
 }
@@ -100,6 +131,34 @@ FadeEffect::getFadeDuration() const
     return value;
 }
 
+void
+FadeEffect::setMaxRange(float value)
+{
+    _maxRange->set( value );
+}
+
+float
+FadeEffect::getMaxRange() const
+{
+    float value = 0.0f;
+    _maxRange->get( value );
+    return value;
+}
+
+void
+FadeEffect::setAttenuationDistance(float value)
+{
+    _attenDist->set( value );
+}
+
+float
+FadeEffect::getAttenuationDistance() const
+{
+    float value = 0.0f;
+    _attenDist->get( value );
+    return value;
+}
+
 //--------------------------------------------------------------------
 
 namespace
@@ -134,7 +193,7 @@ _maxFadeExtent ( 0.0f )
         vp->setFunction(
             "oe_fragFadeLOD",
             FadeLODFragmentShader,
-            ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+            ShaderComp::LOCATION_FRAGMENT_COLORING );
 
         osg::StateSet* ss = getOrCreateStateSet();
 
@@ -148,7 +207,7 @@ FadeLOD::traverse( osg::NodeVisitor& nv )
 {
     if ( nv.getVisitorType() == nv.CULL_VISITOR )
     {
-        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
         PerViewData& data = _perViewData.get(cv);
         if ( !data._opacity.valid() )
         {
diff --git a/src/osgEarth/FileUtils b/src/osgEarth/FileUtils
index daf4c3a..60a1231 100644
--- a/src/osgEarth/FileUtils
+++ b/src/osgEarth/FileUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/FileUtils.cpp b/src/osgEarth/FileUtils.cpp
index 6c14f46..396a5bc 100644
--- a/src/osgEarth/FileUtils.cpp
+++ b/src/osgEarth/FileUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/GeoCommon b/src/osgEarth/GeoCommon
index dc4d106..31e0c8e 100644
--- a/src/osgEarth/GeoCommon
+++ b/src/osgEarth/GeoCommon
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -24,6 +24,15 @@ namespace osgEarth
 #define NO_DATA_VALUE -FLT_MAX
 
     /**
+     * Types of interpolation between two geodetic locations.
+     */
+    enum GeoInterpolation
+    {
+        GEOINTERP_GREAT_CIRCLE,
+        GEOINTERP_RHUMB_LINE
+    };
+
+    /**
      * Elevation interpolation methods.
      */
     enum ElevationInterpolation
@@ -31,7 +40,7 @@ namespace osgEarth
         INTERP_AVERAGE,
         INTERP_NEAREST,
         INTERP_BILINEAR,
-        INTERP_TRIANGULATE        
+        INTERP_TRIANGULATE
     };
 
     /**
diff --git a/src/osgEarth/GeoData b/src/osgEarth/GeoData
index 9d62116..c452ac3 100644
--- a/src/osgEarth/GeoData
+++ b/src/osgEarth/GeoData
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -673,6 +673,16 @@ namespace osgEarth
         const GeoExtent& getExtent() const;
 
         /**
+         * The minimum height in the heightfield
+         */
+        float getMinHeight() const { return _minHeight; }
+
+        /**
+         * The maximum height in the heightfield
+         */
+        float getMaxHeight() const { return _maxHeight; }
+
+        /**
          * Gets a pointer to the underlying OSG heightfield.
          */
         const osg::HeightField* getHeightField() const;
@@ -683,12 +693,37 @@ namespace osgEarth
          */
         osg::HeightField* takeHeightField();
 
+        /**
+         * Gets the X interval of this GeoHeightField
+         */
+        double getXInterval() const;
+
+        /**
+         * Gets the Y interval of this GeoHeightField
+         */
+        double getYInterval() const;
+
+
+        //Sort GeoHeightField's by resolution
+        struct SortByResolutionFunctor
+        {
+            inline bool operator() (const GeoHeightField& lhs, const GeoHeightField& rhs) const
+            {                
+                return lhs.getXInterval() < rhs.getXInterval();                
+            }
+        };
+
     protected:
         osg::ref_ptr<osg::HeightField> _heightField;
         GeoExtent                      _extent;
+        float                          _minHeight, _maxHeight;
     };
 
 	typedef std::vector<GeoHeightField> GeoHeightFieldVector;
+
+
+
+
 }
 
 #endif // OSGEARTH_GEODATA_H
diff --git a/src/osgEarth/GeoData.cpp b/src/osgEarth/GeoData.cpp
index 26ef307..d35454b 100644
--- a/src/osgEarth/GeoData.cpp
+++ b/src/osgEarth/GeoData.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -1681,7 +1681,9 @@ GeoHeightField GeoHeightField::INVALID( 0L, GeoExtent::INVALID );
 
 GeoHeightField::GeoHeightField() :
 _heightField( 0L ),
-_extent     ( GeoExtent::INVALID )
+_extent     ( GeoExtent::INVALID ),
+_minHeight  ( 0.0f ),
+_maxHeight  ( 0.0f )
 {
     //nop
 }
@@ -1691,7 +1693,6 @@ GeoHeightField::GeoHeightField(osg::HeightField* heightField,
 _heightField( heightField ),
 _extent     ( extent )
 {
-
     if ( _heightField.valid() && extent.isInvalid() )
     {
         OE_WARN << LC << "Created with a valid heightfield AND INVALID extent" << std::endl;
@@ -1706,6 +1707,15 @@ _extent     ( extent )
         _heightField->setXInterval( (maxx - minx)/(double)(_heightField->getNumColumns()-1) );
         _heightField->setYInterval( (maxy - miny)/(double)(_heightField->getNumRows()-1) );
         _heightField->setBorderWidth( 0 );
+
+        _minHeight = FLT_MAX, _maxHeight = -FLT_MAX;
+        const osg::HeightField::HeightList& heights = _heightField->getHeightList();
+        for( unsigned i=0; i<heights.size(); ++i )
+        {
+            float h = heights[i];
+            if ( h > _maxHeight ) _maxHeight = h;
+            if ( h < _minHeight ) _minHeight = h;
+        }
     }
 }
 
@@ -1783,8 +1793,6 @@ GeoHeightField::createSubSample( const GeoExtent& destEx, ElevationInterpolation
 
     int w = _heightField->getNumColumns();
     int h = _heightField->getNumRows();
-    //double dx = _heightField->getXInterval() * div;
-    //double dy = _heightField->getYInterval() * div;
     double xInterval = _extent.width() / (double)(_heightField->getNumColumns()-1);
     double yInterval = _extent.height() / (double)(_heightField->getNumRows()-1);
     double dx = xInterval * div;
@@ -1816,17 +1824,6 @@ GeoHeightField::createSubSample( const GeoExtent& destEx, ElevationInterpolation
         }
     }
 
-#if 0
-    for( x = destEx.xMin(), col=0; col < w; x += dx, col++ )
-    {
-        for( y = destEx.yMin(), row=0; row < h; y += dy, row++ )
-        {
-            float height = HeightFieldUtils::getHeightAtLocation( _heightField.get(), x, y, _extent.xMin(), _extent.yMin(), xInterval, yInterval, interpolation);
-            dest->setHeight( col, row, height );
-        }
-    }
-#endif
-
     osg::Vec3d orig( destEx.xMin(), destEx.yMin(), _heightField->getOrigin().z() );
     dest->setOrigin( orig );
 
@@ -1856,3 +1853,17 @@ GeoHeightField::takeHeightField()
 {
     return _heightField.release();
 }
+
+double
+GeoHeightField::getXInterval() const
+{
+    return _extent.width()  / (double)(_heightField->getNumColumns()-1);        
+}
+
+double
+GeoHeightField::getYInterval() const
+{
+    return _extent.height() / (double)(_heightField->getNumRows()-1);
+}
+
+
diff --git a/src/osgEarth/GeoMath b/src/osgEarth/GeoMath
index 867082e..a4f68b6 100644
--- a/src/osgEarth/GeoMath
+++ b/src/osgEarth/GeoMath
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -21,21 +21,13 @@
 #define OSGEARTH_GEOMATH_H 1
 
 #include <osgEarth/Common>
+#include <osgEarth/GeoCommon>
 #include <osgEarth/SpatialReference>
 #include <osg/CoordinateSystemNode>
 
 namespace osgEarth
 {
     /**
-     * Types of interpolation between two geodetic locations.
-     */
-    enum GeoInterpolation
-    {
-        GEOINTERP_GREAT_CIRCLE,
-        GEOINTERP_RHUMB_LINE
-    };
-
-    /**
      * Useful calculations for lat/long points.
      * Converted from http://www.movable-type.co.uk/scripts/latlong.html
      */
diff --git a/src/osgEarth/GeoMath.cpp b/src/osgEarth/GeoMath.cpp
index e4ca3be..b06dc76 100644
--- a/src/osgEarth/GeoMath.cpp
+++ b/src/osgEarth/GeoMath.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -164,7 +164,7 @@ GeoMath::interpolate(double lat1Rad, double lon1Rad,
                      double t,
                      double& out_latRad, double& out_lonRad)
 {
-    static osg::EllipsoidModel em;
+    static osg::EllipsoidModel em; // questionable. make non-static?
 
     osg::Vec3d v0, v1;
 
diff --git a/src/osgEarth/Geoid b/src/osgEarth/Geoid
index e793b55..24dd49e 100644
--- a/src/osgEarth/Geoid
+++ b/src/osgEarth/Geoid
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Geoid.cpp b/src/osgEarth/Geoid.cpp
index 74162da..9da21fb 100644
--- a/src/osgEarth/Geoid.cpp
+++ b/src/osgEarth/Geoid.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/HTTPClient b/src/osgEarth/HTTPClient
index 85f7ece..42ed686 100644
--- a/src/osgEarth/HTTPClient
+++ b/src/osgEarth/HTTPClient
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -219,6 +219,13 @@ namespace osgEarth
            Setting to 0 (default) is infinite timeout */
         void setTimeout( long timeout );
 
+        /**
+         * One time thread safe initialization. In osgEarth, you don't need
+         * to call this directly; osgEarth::Registry will call it at
+         * startup.
+         */
+        static void globalInit();
+
 
     public:
         /**
diff --git a/src/osgEarth/HTTPClient.cpp b/src/osgEarth/HTTPClient.cpp
index b3e3316..0905834 100644
--- a/src/osgEarth/HTTPClient.cpp
+++ b/src/osgEarth/HTTPClient.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -295,6 +295,8 @@ HTTPResponse::getHeadersAsConfig() const
 
 namespace
 {
+    // TODO: consider moving this stuff into the osgEarth::Registry;
+    // don't like it here in the global scope
     // per-thread client map (must be global scope)
     static Threading::PerThread<HTTPClient> s_clientPerThread;
 
@@ -414,6 +416,12 @@ void HTTPClient::setTimeout( long timeout )
 }
 
 void
+HTTPClient::globalInit()
+{
+    curl_global_init(CURL_GLOBAL_ALL);
+}
+
+void
 HTTPClient::readOptions(const osgDB::Options* options, std::string& proxy_host, std::string& proxy_port) const
 {
     // try to set proxy host/port by reading the CURL proxy options
diff --git a/src/osgEarth/HeightFieldUtils b/src/osgEarth/HeightFieldUtils
index a69d41a..bd1b22d 100644
--- a/src/osgEarth/HeightFieldUtils
+++ b/src/osgEarth/HeightFieldUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -90,7 +90,7 @@ namespace osgEarth
          * Resizes a heightfield, keeping the corner values the same and
          * resampling the internal posts.
          */
-        static osg::HeightField* resizeHeightField(
+        static osg::HeightField* resampleHeightField(
             osg::HeightField* input,
             int newX,
             int newY,
diff --git a/src/osgEarth/HeightFieldUtils.cpp b/src/osgEarth/HeightFieldUtils.cpp
index e325984..4cd59e8 100644
--- a/src/osgEarth/HeightFieldUtils.cpp
+++ b/src/osgEarth/HeightFieldUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -270,14 +270,17 @@ HeightFieldUtils::createSubSample(osg::HeightField* input, const GeoExtent& inpu
 }
 
 osg::HeightField*
-HeightFieldUtils::resizeHeightField(osg::HeightField* input, int newColumns, int newRows,
-                                    ElevationInterpolation interp)
+HeightFieldUtils::resampleHeightField(osg::HeightField*      input,
+                                      int                    newColumns, 
+                                      int                    newRows,
+                                      ElevationInterpolation interp)
 {
     if ( newColumns <= 1 && newRows <= 1 )
         return 0L;
 
     if ( newColumns == input->getNumColumns() && newRows == (int)input->getNumRows() )
-        return new osg::HeightField( *input, osg::CopyOp::DEEP_COPY_ALL );
+        return input;
+        //return new osg::HeightField( *input, osg::CopyOp::DEEP_COPY_ALL );
 
     double spanX = (input->getNumColumns()-1) * input->getXInterval();
     double spanY = (input->getNumRows()-1) * input->getYInterval();
diff --git a/src/osgEarth/IOTypes b/src/osgEarth/IOTypes
index 5141f98..e4f6764 100644
--- a/src/osgEarth/IOTypes
+++ b/src/osgEarth/IOTypes
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/IOTypes.cpp b/src/osgEarth/IOTypes.cpp
index 96ea3e5..bb85ba7 100644
--- a/src/osgEarth/IOTypes.cpp
+++ b/src/osgEarth/IOTypes.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ImageLayer b/src/osgEarth/ImageLayer
index 456c70d..0b40f72 100644
--- a/src/osgEarth/ImageLayer
+++ b/src/osgEarth/ImageLayer
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
 #include <osgEarth/ColorFilter>
 #include <osgEarth/TileSource>
 #include <osgEarth/TerrainLayer>
+#include <osgEarth/URI>
 
 namespace osgEarth
 {
@@ -71,8 +72,8 @@ namespace osgEarth
         /**
          * Gets or sets the nodata image for this MapLayer
          */
-        optional<std::string>& noDataImageFilename() { return _noDataImageFilename; }
-        const optional<std::string>& noDataImageFilename() const { return _noDataImageFilename; }
+        optional<URI>& noDataImageFilename() { return _noDataImageFilename; }
+        const optional<URI>& noDataImageFilename() const { return _noDataImageFilename; }
 
         /**
          * Gets the transparent color of this TileSource
@@ -102,13 +103,13 @@ namespace osgEarth
         void fromConfig( const Config& conf );
         void setDefaults();
 
-        optional<float>  _opacity;
-        optional<float>  _minRange;
-        optional<float>  _maxRange;
+        optional<float>       _opacity;
+        optional<float>       _minRange;
+        optional<float>       _maxRange;
         optional<osg::Vec4ub> _transparentColor;
-        optional<std::string> _noDataImageFilename;
-        optional<bool> _lodBlending;
-        ColorFilterChain _colorFilters;
+        optional<URI>         _noDataImageFilename;
+        optional<bool>        _lodBlending;
+        ColorFilterChain      _colorFilters;
     };
 
     //--------------------------------------------------------------------
@@ -250,6 +251,7 @@ namespace osgEarth
 
         CacheBin* getCacheBin( const Profile* profile );
 
+
     protected:
 
         // Creates an image that's in the same profile as the provided key.
diff --git a/src/osgEarth/ImageLayer.cpp b/src/osgEarth/ImageLayer.cpp
index bbf36a3..4e99558 100644
--- a/src/osgEarth/ImageLayer.cpp
+++ b/src/osgEarth/ImageLayer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -164,12 +164,10 @@ ImageLayerTileProcessor::init(const ImageLayerOptions& options,
 
     if ( _options.noDataImageFilename().isSet() && !_options.noDataImageFilename()->empty() )
     {
-        URI noDataURI( *_options.noDataImageFilename() );
-        _noDataImage = noDataURI.getImage( dbOptions );
-        //_noDataImage = URI( *_options.noDataImageFilename() ).readImage(dbOptions).getImage();
+        _noDataImage = _options.noDataImageFilename()->getImage( dbOptions );
         if ( !_noDataImage.valid() )
         {
-            OE_WARN << "Failed to read nodata image from \"" << _options.noDataImageFilename().value() << "\"" << std::endl;
+            OE_WARN << "Failed to read nodata image from \"" << _options.noDataImageFilename()->full() << "\"" << std::endl;
         }
     }
 }
@@ -695,6 +693,12 @@ ImageLayer::createImageFromTileSource(const TileKey&    key,
     {
         result = source->createImage( key, op.get(), progress );
     }
+
+    // Process images with full alpha to properly support MP blending.
+    if ( result != 0L )
+    {
+        ImageUtils::featherAlphaRegions( result.get() );
+    }
     
     // If image creation failed (but was not intentionally canceled),
     // blacklist this tile for future requests.
@@ -703,8 +707,6 @@ ImageLayer::createImageFromTileSource(const TileKey&    key,
         source->getBlacklist()->add( key.getTileId() );
     }
 
-    //return result.release();
-    //return GeoImage(result.get(), finalKey.getExtent());
     return GeoImage(result.get(), key.getExtent());
 }
 
@@ -815,5 +817,11 @@ ImageLayer::assembleImageFromTileSource(const TileKey&    key,
             *_runtimeOptions.driver()->bilinearReprojection());
     }
 
+    // Process images with full alpha to properly support MP blending.
+    if ( result.valid() )
+    {
+        ImageUtils::featherAlphaRegions( result.getImage() );
+    }
+
     return result;
 }
diff --git a/src/osgEarth/ImageMosaic b/src/osgEarth/ImageMosaic
index 15a868e..2a654dc 100644
--- a/src/osgEarth/ImageMosaic
+++ b/src/osgEarth/ImageMosaic
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -30,7 +30,7 @@ namespace osgEarth
     /**
      * Pairs an OSG image with TileKey parameters.
      */
-    struct TileImage
+    struct OSGEARTH_EXPORT TileImage
     {
         /**
         *Constructor
@@ -54,7 +54,7 @@ namespace osgEarth
     /**
      * Utility class for extracting a single image from a collection of image tiles
      */
-    class ImageMosaic : public osg::Referenced
+    class OSGEARTH_EXPORT ImageMosaic : public osg::Referenced
     {
     public:
         ImageMosaic();
diff --git a/src/osgEarth/ImageMosaic.cpp b/src/osgEarth/ImageMosaic.cpp
index a4e3ba2..643fd58 100644
--- a/src/osgEarth/ImageMosaic.cpp
+++ b/src/osgEarth/ImageMosaic.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ImageToHeightFieldConverter b/src/osgEarth/ImageToHeightFieldConverter
index bda9202..4886d41 100644
--- a/src/osgEarth/ImageToHeightFieldConverter
+++ b/src/osgEarth/ImageToHeightFieldConverter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ImageToHeightFieldConverter.cpp b/src/osgEarth/ImageToHeightFieldConverter.cpp
index 4ae26d8..59ca884 100644
--- a/src/osgEarth/ImageToHeightFieldConverter.cpp
+++ b/src/osgEarth/ImageToHeightFieldConverter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ImageUtils b/src/osgEarth/ImageUtils
index 4fd2d9b..c5d68a1 100644
--- a/src/osgEarth/ImageUtils
+++ b/src/osgEarth/ImageUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -151,6 +151,11 @@ namespace osgEarth
         static osg::Image* createEmptyImage();
 
         /**
+         * Creates a one-pixel image.
+         */
+        static osg::Image* createOnePixelImage(const osg::Vec4& color);
+
+        /**
          * Tests an image to see whether it's "empty", i.e. completely transparent,
          * within an alpha threshold.
          */
@@ -204,11 +209,36 @@ namespace osgEarth
         static bool hasAlphaChannel( const osg::Image* image );
 
         /**
+         * Checks whether an image has transparency; i.e. whether
+         * there are any pixels with an alpha component whole value
+         * falls below the specified threshold.
+         */
+        static bool hasTransparency(const osg::Image* image, float alphaThreshold =1.0f);
+
+        /**
+         * Finds pixels with alpha less than [maxAlpha] and sets their color
+         * to match that or neighboring non-alpha pixels. This facilitates multipass
+         * blending or abutting tiles by overlapping them slightly. Specify "maxAlpha"
+         * as the maximum value to consider when searching for fully-transparent pixels.
+         */
+        static void featherAlphaRegions(osg::Image* image, float maxAlpha =0.0f);
+
+        /**
+         * Converts an image (in place) to premultiplied-alpha format.
+         */
+        static void convertToPremultipliedAlpha(osg::Image* image);
+
+        /**
          * Checks whether the given image is compressed
          */
         static bool isCompressed( const osg::Image* image );
 
         /**
+         * Generated a bump map image for the input image
+         */
+        static osg::Image* createBumpMap( const osg::Image* input );
+
+        /**
          * Reads color data out of an image, regardles of its internal pixel format.
          */
         class OSGEARTH_EXPORT PixelReader
@@ -290,7 +320,7 @@ namespace osgEarth
             /**
              * Traverse an image, and call this method on the superclass:
              *
-             *   bool operator(const osg::Vec4& pixel);
+             *   bool operator(osg::Vec4& pixel);
              *
              * If that method returns true, write the value back at the same location.
              */
diff --git a/src/osgEarth/ImageUtils.cpp b/src/osgEarth/ImageUtils.cpp
index 2811f69..106e23c 100644
--- a/src/osgEarth/ImageUtils.cpp
+++ b/src/osgEarth/ImageUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
  */
 
 #include <osgEarth/ImageUtils>
+#include <osgEarth/ThreadingUtils>
 #include <osg/Notify>
 #include <osg/Texture>
 #include <osg/ImageSequence>
@@ -112,6 +113,56 @@ ImageUtils::copyAsSubImage(const osg::Image* src, osg::Image* dst, int dst_start
     return true;
 }  
 
+osg::Image*
+ImageUtils::createBumpMap(const osg::Image* input)
+{
+    osg::Image* output = osg::clone(input, osg::CopyOp::DEEP_COPY_ALL);
+
+    static const float kernel[] = {
+        -1.0, -1.0, 0.0,
+        -1.0,  0.0, 1.0,
+         0.0,  1.0, 1.0 
+    };
+
+    PixelReader read(input);
+    PixelWriter write(output);
+
+    osg::Vec4f mid(0.5f,0.5f,0.5f,0.5f);
+
+    for( int t=0; t<input->t(); ++t )
+    {
+        for( int s=0; s<input->s(); ++s )
+        {
+            if ( t == 0 || t == input->t()-1 || s == 0 || s == input->s()-1 )
+            {
+                write( mid, s, t );
+            }
+            else
+            {
+                osg::Vec4f sum;
+
+                // run the emboss kernel:
+                for( int tt=0; tt<=2; ++tt )
+                    for( int ss=0; ss<=2; ++ss )
+                        sum += read(s+ss-1,t+tt-1) * kernel[tt*3+ss];
+                sum /= 9.0f;
+
+                // bias for bumpmapping:
+                sum += osg::Vec4f(0.5f,0.5f,0.5f,0.5f);
+
+                // convert to greyscale:
+                sum.r() *= 0.2989f;
+                sum.g() *= 0.5870f;
+                sum.b() *= 0.1140f;
+
+                sum.a() = read(s,t).a();
+                write( sum, s, t );
+            }
+        }
+    }
+    return output;
+}
+
 bool
 ImageUtils::resizeImage(const osg::Image* input, 
                         unsigned int out_s, unsigned int out_t, 
@@ -385,25 +436,28 @@ ImageUtils::sharpenImage( const osg::Image* input )
     return output;
 }
 
+namespace
+{
+    static Threading::Mutex         s_emptyImageMutex;
+    static osg::ref_ptr<osg::Image> s_emptyImage;
+}
 
 osg::Image*
 ImageUtils::createEmptyImage()
 {
-    static OpenThreads::Mutex s_mutex;
-    static osg::ref_ptr< osg::Image> s_image;
-    if (!s_image.valid())
+    if (!s_emptyImage.valid())
     {
-        OpenThreads::ScopedLock< OpenThreads::Mutex > lock( s_mutex );
-        if (!s_image.valid())
+        Threading::ScopedMutexLock exclusive( s_emptyImageMutex );
+        if (!s_emptyImage.valid())
         {
-            s_image = new osg::Image;
-            s_image->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE);
-            s_image->setInternalTextureFormat( GL_RGB8A_INTERNAL );
-            unsigned char *data = s_image->data(0,0);
+            s_emptyImage = new osg::Image;
+            s_emptyImage->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE);
+            s_emptyImage->setInternalTextureFormat( GL_RGB8A_INTERNAL );
+            unsigned char *data = s_emptyImage->data(0,0);
             memset(data, 0, 4);
         }     
     }
-    return s_image.get();
+    return s_emptyImage.get();
 }
 
 bool
@@ -425,6 +479,18 @@ ImageUtils::isEmptyImage(const osg::Image* image, float alphaThreshold)
     return true;    
 }
 
+
+osg::Image*
+ImageUtils::createOnePixelImage(const osg::Vec4& color)
+{
+    osg::Image* image = new osg::Image;
+    image->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE);
+    image->setInternalTextureFormat( GL_RGB8A_INTERNAL );
+    PixelWriter write(image);
+    write(color, 0, 0);
+    return image;
+}
+
 bool
 ImageUtils::isSingleColorImage(const osg::Image* image, float threshold)
 {
@@ -541,6 +607,101 @@ ImageUtils::hasAlphaChannel(const osg::Image* image)
         image->getPixelFormat() == GL_LUMINANCE_ALPHA );
 }
 
+
+bool
+ImageUtils::hasTransparency(const osg::Image* image, float threshold)
+{
+    if ( !image ) return false;
+    PixelReader read(image);
+    for( int t=0; t<image->t(); ++t )
+        for( int s=0; s<image->s(); ++s )
+            if ( read(s, t).a() < threshold )
+                return true;
+    return false;
+}
+
+
+void
+ImageUtils::featherAlphaRegions(osg::Image* image, float maxAlpha)
+{
+    PixelReader read (image);
+    PixelWriter write(image);
+
+    int ns = image->s();
+    int nt = image->t();
+
+    osg::Vec4 n;
+
+    for( int t=0; t<nt; ++t )
+    {
+        bool rowdone = false;
+        for( int s=0; s<ns && !rowdone; ++s )
+        {
+            osg::Vec4 pixel = read(s, t);
+            if ( pixel.a() <= maxAlpha )
+            {
+                bool wrote = false;
+                if ( s < ns-1 ) {
+                    n = read( s+1, t );
+                    if ( n.a() > maxAlpha ) {
+                        write( n, s, t );
+                        wrote = true;
+                    }
+                }
+                if ( !wrote && s > 0 ) {
+                    n = read( s-1, t );
+                    if ( n.a() > maxAlpha ) {
+                        write( n, s, t );
+                        rowdone = true;
+                    }
+                }
+            }
+        }
+    }
+
+    for( int s=0; s<ns; ++s )
+    {
+        bool coldone = false;
+        for( int t=0; t<nt && !coldone; ++t )
+        {
+            osg::Vec4 pixel = read(s, t);
+            if ( pixel.a() <= maxAlpha )
+            {
+                bool wrote = false;
+                if ( t < nt-1 ) {
+                    n = read( s, t+1 );
+                    if ( n.a() > maxAlpha ) {
+                        write( n, s, t );
+                        wrote = true;
+                    }
+                }
+                if ( !wrote && t > 0 ) {
+                    n = read( s, t-1 );
+                    if ( n.a() > maxAlpha ) {
+                        write( n, s, t );
+                        coldone = true;
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+void
+ImageUtils::convertToPremultipliedAlpha(osg::Image* image)
+{
+    PixelReader read(image);
+    PixelWriter write(image);
+    for(int s=0; s<image->s(); ++s) {
+        for( int t=0; t<image->t(); ++t ) {
+            osg::Vec4f c = read(s, t);
+            write( osg::Vec4f(c.r()*c.a(), c.g()*c.a(), c.b()*c.a(), c.a()), s, t);
+        }
+    }
+}
+
+
 bool
 ImageUtils::isCompressed(const osg::Image *image)
 {
diff --git a/src/osgEarth/JsonUtils b/src/osgEarth/JsonUtils
index cf91eb1..33e74d3 100644
--- a/src/osgEarth/JsonUtils
+++ b/src/osgEarth/JsonUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/JsonUtils.cpp b/src/osgEarth/JsonUtils.cpp
index 982e168..633d8d2 100644
--- a/src/osgEarth/JsonUtils.cpp
+++ b/src/osgEarth/JsonUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Layer b/src/osgEarth/Layer
index 739eaca..354b891 100644
--- a/src/osgEarth/Layer
+++ b/src/osgEarth/Layer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -24,6 +24,8 @@
 
 namespace osgEarth
 {
+    class SequenceControl;
+
     /**
      * Base class for all Map layers. 
      */
@@ -40,6 +42,12 @@ namespace osgEarth
          */
         const UID getUID() const { return _uid; }
 
+        /**
+         * Sequence controller if the layer has one.
+         */
+        virtual SequenceControl* getSequenceControl() { return 0L; }
+
+
     private:
         UID _uid;
     };
diff --git a/src/osgEarth/Layer.cpp b/src/osgEarth/Layer.cpp
index 2c995a1..a90b84d 100644
--- a/src/osgEarth/Layer.cpp
+++ b/src/osgEarth/Layer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/LineFunctor b/src/osgEarth/LineFunctor
index 61a4a58..0597fb4 100644
--- a/src/osgEarth/LineFunctor
+++ b/src/osgEarth/LineFunctor
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/LocalTangentPlane b/src/osgEarth/LocalTangentPlane
index 240602b..769e725 100644
--- a/src/osgEarth/LocalTangentPlane
+++ b/src/osgEarth/LocalTangentPlane
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/LocalTangentPlane.cpp b/src/osgEarth/LocalTangentPlane.cpp
index 4b973ad..d3e0c31 100644
--- a/src/osgEarth/LocalTangentPlane.cpp
+++ b/src/osgEarth/LocalTangentPlane.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Locators b/src/osgEarth/Locators
index d362028..38e2c1c 100644
--- a/src/osgEarth/Locators
+++ b/src/osgEarth/Locators
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Locators.cpp b/src/osgEarth/Locators.cpp
index b5116f3..81221c4 100644
--- a/src/osgEarth/Locators.cpp
+++ b/src/osgEarth/Locators.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Map b/src/osgEarth/Map
index db46ef8..4a30f54 100644
--- a/src/osgEarth/Map
+++ b/src/osgEarth/Map
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -166,9 +166,27 @@ namespace osgEarth
          */
         void addMapCallback( MapCallback* callback ) const;
 
+        /**
+         * Removes a callback previously added with addMapCallback.
+         */
         void removeMapCallback( MapCallback* callback );
 
         /**
+         * Begin a batch-update operation. Call this if you intend to add multiple
+         * layers at once; then call endUpdate() to complete the operation.
+         * Between calls to beginUpdate() and endUpdate(), Map will not invoke
+         * any callbacks you added wtih addMapCallback.
+         */
+        void beginUpdate();
+
+        /**
+         * Complete a batch update operation that started with a call to
+         * beginUpdate(). Fires all callbacks for operations that occurred
+         * since the call to beginUpdate().
+         */
+        void endUpdate();
+
+        /**
          * Adds an image layer to the map.
          */
         void addImageLayer( ImageLayer* layer );
diff --git a/src/osgEarth/Map.cpp b/src/osgEarth/Map.cpp
index 217e2cb..9819619 100644
--- a/src/osgEarth/Map.cpp
+++ b/src/osgEarth/Map.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -83,6 +83,12 @@ _dataModelRevision   ( 0 )
     // store the IO information in the top-level DB Options:
     _mapOptions.cachePolicy()->apply( _dbOptions.get() );
     URIContext( _mapOptions.referrer() ).apply( _dbOptions.get() );
+
+    // apply an express tile size if there is one.
+    if ( _mapOptions.elevationTileSize().isSet() )
+    {
+        _elevationLayers.setExpressTileSize( *_mapOptions.elevationTileSize() );
+    }
 }
 
 Map::~Map()
@@ -297,16 +303,16 @@ Map::getCache() const
     {
         Cache* cache = 0L;
         
-        // if a cache is defined in the options, use that.
-        if ( _mapOptions.cache().isSet() )
+        // if there's a cache in the registry, install it now.
+        if ( Registry::instance()->getCache() )
         {
-            cache = CacheFactory::create( _mapOptions.cache().get() );
+            cache = Registry::instance()->getCache();
         }
 
-        // or, if there's a cache in the registry, install it now.
-        else if ( Registry::instance()->getCache() )
+        // or, if a cache is defined in the options, use that.
+        else if ( _mapOptions.cache().isSet() )
         {
-            cache = Registry::instance()->getCache();
+            cache = CacheFactory::create( _mapOptions.cache().get() );
         }
 
         if ( cache )
@@ -333,13 +339,11 @@ Map::setCache( Cache* cache )
         for (ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i)
         {
             i->get()->setDBOptions( _dbOptions.get() );
-            //i->get()->setCache( _cache.get() );
         }
 
         for (ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i)
         {
             i->get()->setDBOptions( _dbOptions.get() );
-            //i->get()->setCache( _cache.get() );
         }
     }
 }
@@ -358,7 +362,29 @@ Map::removeMapCallback( MapCallback* cb )
     if (i != _mapCallbacks.end())
     {
         _mapCallbacks.erase( i );
-    }    
+    }
+}
+
+void
+Map::beginUpdate()
+{
+    MapModelChange msg( MapModelChange::BEGIN_BATCH_UPDATE, _dataModelRevision );
+
+    for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ )
+    {
+        i->get()->onMapModelChanged( msg );
+    }
+}
+
+void
+Map::endUpdate()
+{
+    MapModelChange msg( MapModelChange::END_BATCH_UPDATE, _dataModelRevision );
+ 
+    for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ )
+    {
+        i->get()->onMapModelChanged( msg );
+    }
 }
 
 void
@@ -1064,10 +1090,13 @@ Map::sync( MapFrame& frame ) const
 
         if ( frame._parts & ELEVATION_LAYERS )
         {
-            if ( !frame._initialized )
-                frame._elevationLayers.reserve( _elevationLayers.size() );
-            frame._elevationLayers.clear();
-            std::copy( _elevationLayers.begin(), _elevationLayers.end(), std::back_inserter(frame._elevationLayers) );
+            frame._elevationLayers = _elevationLayers;
+            //if ( !frame._initialized )
+            //    frame._elevationLayers.reserve( _elevationLayers.size() );
+            //frame._elevationLayers.clear();
+            //std::copy( _elevationLayers.begin(), _elevationLayers.end(), std::back_inserter(frame._elevationLayers) );
+            if ( _mapOptions.elevationTileSize().isSet() )
+                frame._elevationLayers.setExpressTileSize( *_mapOptions.elevationTileSize() );
         }
 
         if ( frame._parts & MODEL_LAYERS )
diff --git a/src/osgEarth/MapCallback b/src/osgEarth/MapCallback
index 21ad4b3..c4be1d7 100644
--- a/src/osgEarth/MapCallback
+++ b/src/osgEarth/MapCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -40,6 +40,10 @@ namespace osgEarth
     {
         virtual void onMapInfoEstablished( const MapInfo& mapInfo ) { } 
 
+        // callbacks for begin/end batch update operations.
+        virtual void onBeginUpdate() { }
+        virtual void onEndUpdate() { }
+
         virtual void onMapModelChanged( const MapModelChange& change );
 
         virtual void onImageLayerAdded( ImageLayer* layer, unsigned int index ) { }
diff --git a/src/osgEarth/MapCallback.cpp b/src/osgEarth/MapCallback.cpp
index bd44174..dd654a7 100644
--- a/src/osgEarth/MapCallback.cpp
+++ b/src/osgEarth/MapCallback.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MapFrame b/src/osgEarth/MapFrame
index 650a8ed..16c55a8 100644
--- a/src/osgEarth/MapFrame
+++ b/src/osgEarth/MapFrame
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -54,6 +54,11 @@ namespace osgEarth
          */
         bool sync();
 
+        /**
+         * True is a sync() would acquire new data.
+         */
+        bool needsSync() const;
+
         /** Accesses the profile/rendering info about the source map. */
         const MapInfo& getMapInfo() const { return _mapInfo; }
 
diff --git a/src/osgEarth/MapFrame.cpp b/src/osgEarth/MapFrame.cpp
index a39a9a8..672de8c 100644
--- a/src/osgEarth/MapFrame.cpp
+++ b/src/osgEarth/MapFrame.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -72,6 +72,15 @@ MapFrame::sync()
 
 
 bool
+MapFrame::needsSync() const
+{
+    return
+        (_map.valid()) &&
+        (_map->getDataModelRevision() != _mapDataModelRevision || !_initialized);
+}
+
+
+bool
 MapFrame::getHeightField(const TileKey&                  key,
                          bool                            fallback,
                          osg::ref_ptr<osg::HeightField>& out_hf,
diff --git a/src/osgEarth/MapInfo b/src/osgEarth/MapInfo
index b045e89..a1d325d 100644
--- a/src/osgEarth/MapInfo
+++ b/src/osgEarth/MapInfo
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MapInfo.cpp b/src/osgEarth/MapInfo.cpp
index 9284072..83b47b9 100644
--- a/src/osgEarth/MapInfo.cpp
+++ b/src/osgEarth/MapInfo.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MapModelChange b/src/osgEarth/MapModelChange
index 5b92462..622267b 100644
--- a/src/osgEarth/MapModelChange
+++ b/src/osgEarth/MapModelChange
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -36,6 +36,8 @@ namespace osgEarth
     {
         enum ActionType
         {
+            BEGIN_BATCH_UPDATE,               // signals the start of multiple changes
+            END_BATCH_UPDATE,                 // signals the end of multiple changes
             ADD_IMAGE_LAYER,
             REMOVE_IMAGE_LAYER,
             MOVE_IMAGE_LAYER,
@@ -50,7 +52,7 @@ namespace osgEarth
             UNSPECIFIED
         };
 
-        MapModelChange( ActionType action, Revision mapModeRev, Layer* layer, int firstIndex =-1, int secondIndex =-1 ) 
+        MapModelChange( ActionType action, Revision mapModeRev, Layer* layer =0L, int firstIndex =-1, int secondIndex =-1 ) 
             : _action(action), _layer(layer), _modelRevision(mapModeRev), _firstIndex(firstIndex), _secondIndex(secondIndex) { }
 
         const ActionType& getAction() const { return _action; }
@@ -64,10 +66,10 @@ namespace osgEarth
         MaskLayer* getMaskLayer() const { return dynamic_cast<MaskLayer*>(_layer.get()); }
 
     private:
-        ActionType _action;
+        ActionType          _action;
         osg::ref_ptr<Layer> _layer;
-        Revision _modelRevision;
-        int _firstIndex, _secondIndex;
+        Revision            _modelRevision;
+        int                 _firstIndex, _secondIndex;
     };
 }
 
diff --git a/src/osgEarth/MapNode b/src/osgEarth/MapNode
index 3544221..87344ed 100644
--- a/src/osgEarth/MapNode
+++ b/src/osgEarth/MapNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -24,12 +24,13 @@
 #include <osgEarth/Map>
 #include <osgEarth/MapNodeOptions>
 #include <osgEarth/SpatialReference>
+#include <osgEarth/TerrainEngineNode>
 
 namespace osgEarth
 {
     class OverlayDecorator;
     class Terrain;
-    class TerrainEngineNode;
+    class MapNodeCullData;
 
     /**
      * OSG Node that forms the root of an osgEarth map. This node is a "view" component
@@ -107,7 +108,8 @@ namespace osgEarth
          * @param travMask
          *      Traversal mask to apply while searching
          */
-        static MapNode* findMapNode( osg::Node* graph, unsigned travMask =~0 );  
+        static MapNode* findMapNode( osg::Node* graph, unsigned travMask =~0 );
+        static MapNode* get( osg::Node* graph, unsigned travMask =~0 ) { return findMapNode(graph, travMask); }
 
         /**
          * Returns true if the realized terrain model is geocentric, false if
@@ -142,18 +144,6 @@ namespace osgEarth
         OverlayDecorator* getOverlayDecorator() { return _overlayDecorator; }
 
         /**
-         * Accesses the group containing anything that will be "draped" on the terrain
-         * via the OverlayDecorator.
-         */
-        osg::Group* getOverlayGroup() { return _overlayModels.get(); }
-        
-        /**
-         * If you add or remove children to/fro the overlay group above, call this
-         * method to refresh the overlay graph.
-         */
-        void updateOverlayGraph();
-
-        /**
          * Gets the engine properties associated with this node. The engine
          * properties dictate how the map engine will create scene graph elements.
          */
@@ -215,7 +205,7 @@ namespace osgEarth
         ModelLayerNodeMap        _modelLayerNodes;
         osg::Group*              _maskLayerNode;
         unsigned                 _lastNumBlacklistedFilenames;
-        TerrainEngineNode*       _terrainEngine;
+        osg::ref_ptr<TerrainEngineNode> _terrainEngine;
         bool                     _terrainEngineInitialized;
         osg::Group*              _terrainEngineContainer;
 
@@ -238,9 +228,14 @@ namespace osgEarth
     private:
 
         osg::ref_ptr< ModelLayerCallback > _modelLayerCallback;
-        osg::ref_ptr< MapCallback > _mapCallback;
+        osg::ref_ptr< MapCallback >        _mapCallback;
     
         void init();
+
+        MapNodeCullData* getCullData(osg::Camera* camera) const;
+        typedef std::map<osg::Camera*, osg::ref_ptr<MapNodeCullData> > CullDataMap;
+        mutable CullDataMap _cullData;
+        mutable Threading::ReadWriteMutex _cullDataMutex;
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/MapNode.cpp b/src/osgEarth/MapNode.cpp
index ff75556..4ddebe1 100644
--- a/src/osgEarth/MapNode.cpp
+++ b/src/osgEarth/MapNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,16 +18,22 @@
 */
 #include <osgEarth/MapNode>
 #include <osgEarth/Capabilities>
+#include <osgEarth/ClampableNode>
+#include <osgEarth/ClampingTechnique>
+#include <osgEarth/CullingUtils>
+#include <osgEarth/DrapeableNode>
+#include <osgEarth/DrapingTechnique>
 #include <osgEarth/MapNodeObserver>
 #include <osgEarth/MaskNode>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Registry>
+#include <osgEarth/ShaderFactory>
+#include <osgEarth/TraversalData>
 #include <osgEarth/VirtualProgram>
 #include <osgEarth/OverlayDecorator>
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarth/TextureCompositor>
 #include <osgEarth/URI>
-#include <osgEarth/DrapeableNode>
 #include <osg/ArgumentParser>
 #include <osg/PagedLOD>
 
@@ -131,8 +137,16 @@ public:
                   osg::ref_ptr< MapNode::TileRangeData > ranges = static_cast< MapNode::TileRangeData* >(node.getUserData());
                   if (ranges)
                   {
-                      node.setRange(0, ranges->_minRange, ranges->_maxRange);
-                      node.setRange(1, 0, ranges->_minRange);
+                      if (node.getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN)
+                      {
+                          node.setRange( 0, ranges->_minRange, ranges->_maxRange );
+                          node.setRange( 1, ranges->_maxRange, FLT_MAX );                          
+                      }
+                      else
+                      {                          
+                          node.setRange(0, ranges->_minRange, ranges->_maxRange);
+                          node.setRange(1, 0, ranges->_minRange);
+                      }
                   }                  
               }
               
@@ -225,7 +239,7 @@ MapNode::init()
     // Since we have global uniforms in the stateset, mark it dynamic so it is immune to
     // multi-threaded overlap
     // TODO: do we need this anymore? there are no more global uniforms in here.. gw
-    getOrCreateStateSet()->setDataVariance(osg::Object::DYNAMIC);
+    //getOrCreateStateSet()->setDataVariance(osg::Object::DYNAMIC);
 
     _modelLayerCallback = new MapModelLayerCallback(this);
 
@@ -301,22 +315,31 @@ MapNode::init()
     _models->setName( "osgEarth::MapNode.modelsGroup" );
     addChild( _models.get() );
 
-    // make a group for overlay model layers:
-    _overlayModels = new ObserverGroup(); //osg::Group();
-    _overlayModels->setName( "osgEarth::MapNode.overlayModelsGroup" );
-
     // a decorator for overlay models:
     _overlayDecorator = new OverlayDecorator();
     _overlayDecorator->setOverlayGraphTraversalMask( terrainOptions.secondaryTraversalMask().value() );
 
-    if ( _mapNodeOptions.overlayBlending().isSet() )
-        _overlayDecorator->setOverlayBlending( *_mapNodeOptions.overlayBlending() );
-    if ( _mapNodeOptions.overlayTextureSize().isSet() )
-        _overlayDecorator->setTextureSize( *_mapNodeOptions.overlayTextureSize() );
-    if ( _mapNodeOptions.overlayMipMapping().isSet() )
-        _overlayDecorator->setMipMapping( *_mapNodeOptions.overlayMipMapping() );
-    if ( _mapNodeOptions.overlayAttachStencil().isSet() )
-        _overlayDecorator->setAttachStencil( *_mapNodeOptions.overlayAttachStencil() );
+    // install the Draping technique for overlays:
+    {
+        DrapingTechnique* draping = new DrapingTechnique();
+
+        if ( _mapNodeOptions.overlayBlending().isSet() )
+            draping->setOverlayBlending( *_mapNodeOptions.overlayBlending() );
+        if ( _mapNodeOptions.overlayTextureSize().isSet() )
+            draping->setTextureSize( *_mapNodeOptions.overlayTextureSize() );
+        if ( _mapNodeOptions.overlayMipMapping().isSet() )
+            draping->setMipMapping( *_mapNodeOptions.overlayMipMapping() );
+        if ( _mapNodeOptions.overlayAttachStencil().isSet() )
+            draping->setAttachStencil( *_mapNodeOptions.overlayAttachStencil() );
+
+        _overlayDecorator->addTechnique( draping );
+    }
+
+    // install the Clamping technique for overlays:
+    {
+        _overlayDecorator->addTechnique( new ClampingTechnique() );
+    }
+
 
     addTerrainDecorator( _overlayDecorator );
 
@@ -347,14 +370,9 @@ 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( 0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
+        Registry::instance()->getShaderFactory()->installLightingShaders( vp );
         ss->setAttributeAndModes( vp, osg::StateAttribute::ON );
     }
 
@@ -562,7 +580,6 @@ MapNode::onModelLayerRemoved( ModelLayer* layer )
             else
             {
                 _models->removeChild( node );
-                updateOverlayGraph();
             }
             
             _modelLayerNodes.erase( i );
@@ -582,16 +599,9 @@ MapNode::onModelLayerMoved( ModelLayer* layer, unsigned int oldIndex, unsigned i
         if ( i != _modelLayerNodes.end() )
         {
             osg::ref_ptr<osg::Node> node = i->second;
-            
-            //if ( dynamic_cast<osgSim::OverlayNode*>( node ) )
-            //{
-            //    // treat overlay node as a special case
-            //}
-            //else
-            {
-                _models->removeChild( node.get() );
-                _models->insertChild( newIndex, node.get() );
-            }
+
+            _models->removeChild( node.get() );
+            _models->insertChild( newIndex, node.get() );
         }
         
         dirtyBound();
@@ -663,12 +673,60 @@ MapNode::traverse( osg::NodeVisitor& nv )
             //Only remove the blacklisted filenames if new filenames have been added since last time.
             _lastNumBlacklistedFilenames = numBlacklist;
             RemoveBlacklistedFilenamesVisitor v;
-            //accept( v );
             _terrainEngine->accept( v );
         }
+
+        // traverse:
+        std::for_each( _children.begin(), _children.end(), osg::NodeAcceptOp(nv) );
     }
 
-    osg::Group::traverse( nv );
+    else if ( nv.getVisitorType() == nv.CULL_VISITOR )
+    {
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
+        if ( cv )
+        {
+            // insert traversal data for this camera:
+            osg::ref_ptr<osg::Referenced> oldUserData = cv->getUserData();
+            MapNodeCullData* cullData = getCullData( cv->getCurrentCamera() );
+            cv->setUserData( cullData );
+
+            cullData->_mapNode = this;
+
+            // calculate altitude:
+            osg::Vec3d eye = cv->getViewPoint();
+            const SpatialReference* srs = getMapSRS();
+            if ( srs && !srs->isProjected() )
+            {
+                GeoPoint ecef;
+                ecef.fromWorld( srs, eye );
+                cullData->_cameraAltitude = ecef.alt();
+            }
+            else
+            {
+                cullData->_cameraAltitude = eye.z();
+            }
+
+            // window scale matrix:
+            osg::Matrix  m4 = cv->getWindowMatrix();
+            osg::Matrix3 m3( m4(0,0), m4(1,0), m4(2,0),
+                             m4(0,1), m4(1,1), m4(2,1),
+                             m4(0,2), m4(1,2), m4(2,2) );
+            cullData->_windowScaleMatrix->set( m3 );
+
+            // traverse:
+            cv->pushStateSet( cullData->_stateSet.get() );
+            std::for_each( _children.begin(), _children.end(), osg::NodeAcceptOp(nv) );
+            cv->popStateSet();
+
+            // restore:
+            cv->setUserData( oldUserData.get() );
+        }
+    }
+
+    else
+    {
+        osg::Group::traverse( nv );
+    }
 }
 
 void
@@ -677,44 +735,40 @@ MapNode::onModelLayerOverlayChanged( ModelLayer* layer )
     osg::ref_ptr<osg::Node> node = _modelLayerNodes[ layer ];
     if ( node.get() )
     {
-        DrapeableNode* draper = dynamic_cast<DrapeableNode*>(node.get());
-        if ( !draper && layer->getOverlay() )
+        OverlayNode* overlay = dynamic_cast<OverlayNode*>(node.get());
+        if ( !overlay && layer->getOverlay() )
         {
-            draper = new DrapeableNode(this);
-            draper->addChild( node.get() );
-            _models->replaceChild( node.get(), draper );
+            overlay = new DrapeableNode(this);
+            overlay->addChild( node.get() );
+            _models->replaceChild( node.get(), overlay );
         }
-        else
+        else if ( overlay )
         {
-            draper->setDraped( layer->getOverlay() );
+            overlay->setActive( layer->getOverlay() );
         }
     }
-
-    //OE_NOTICE << "Overlay changed to "  << layer->getOverlay() << std::endl;
-    //osg::ref_ptr< osg::Group > origParent = layer->getOverlay() ? _models.get() : _overlayModels.get();
-    //osg::ref_ptr< osg::Group > newParent  = layer->getOverlay() ? _overlayModels.get() : _models.get();
-
-    //osg::ref_ptr< osg::Node > node = layer->getOrCreateNode();
-    //if (node.valid())
-    //{
-    //    //Remove it from the original parent and add it to the new parent
-    //    origParent->removeChild( node.get() );
-    //    newParent->addChild( node.get() );
-    //}
-
-    updateOverlayGraph();
 }
 
-void
-MapNode::updateOverlayGraph()
+MapNodeCullData*
+MapNode::getCullData(osg::Camera* key) const
 {
-    if ( _overlayModels->getNumChildren() > 0 && _overlayDecorator->getOverlayGraph() != _overlayModels.get() )
+    // first look it up:
     {
-        _overlayDecorator->setOverlayGraph( _overlayModels.get() );
+        Threading::ScopedReadLock shared( _cullDataMutex );
+        CullDataMap::const_iterator i = _cullData.find( key );
+        if ( i != _cullData.end() )
+            return i->second.get();
     }
-    else if ( _overlayModels->getNumChildren() == 0 && _overlayDecorator->getOverlayGraph() == _overlayModels.get() )
+    // if it's not there, make it, but double-check.
     {
-        _overlayDecorator->setOverlayGraph( 0L );
+        Threading::ScopedWriteLock exclusive( _cullDataMutex );
+        
+        CullDataMap::const_iterator i = _cullData.find( key );
+        if ( i != _cullData.end() )
+            return i->second.get();
+
+        MapNodeCullData* cullData = new MapNodeCullData();
+        _cullData[key] = cullData;
+        return cullData;
     }
 }
-
diff --git a/src/osgEarth/MapNodeObserver b/src/osgEarth/MapNodeObserver
index 2e71ff0..08a3e41 100644
--- a/src/osgEarth/MapNodeObserver
+++ b/src/osgEarth/MapNodeObserver
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MapNodeOptions b/src/osgEarth/MapNodeOptions
index 5915971..d0b7979 100644
--- a/src/osgEarth/MapNodeOptions
+++ b/src/osgEarth/MapNodeOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MapNodeOptions.cpp b/src/osgEarth/MapNodeOptions.cpp
index 00e48e3..fc83ecf 100644
--- a/src/osgEarth/MapNodeOptions.cpp
+++ b/src/osgEarth/MapNodeOptions.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MapOptions b/src/osgEarth/MapOptions
index 4a2cd15..43be06d 100644
--- a/src/osgEarth/MapOptions
+++ b/src/osgEarth/MapOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -48,7 +48,8 @@ namespace osgEarth
               _cachePolicy           ( ),
               _cstype                ( CSTYPE_GEOCENTRIC ),
               _referenceURI          ( "" ),
-              _elevationInterpolation( INTERP_BILINEAR )
+              _elevationInterpolation( INTERP_BILINEAR ),
+              _elevTileSize          ( 8 )
         {
             fromConfig(_conf);
         }
@@ -94,6 +95,13 @@ namespace osgEarth
         optional<ElevationInterpolation>& elevationInterpolation(void) { return _elevationInterpolation; }
         const optional<ElevationInterpolation>& elevationInterpolation(void) const { return _elevationInterpolation;}
 
+        /**
+         * Dimension of elevation tiles. By default, the engine will return 
+         * elevation tiles sized to the largest available source tile. This forces
+         * a set size.
+         */
+        optional<unsigned>& elevationTileSize() { return _elevTileSize; }
+        const optional<unsigned>& elevationTileSize() const { return _elevTileSize; }
 
     public:
         /**
@@ -114,13 +122,14 @@ namespace osgEarth
     private:
         void fromConfig( const Config& conf );
 
-        optional<std::string>          _name;
-        optional<ProfileOptions>       _profileOptions;
-        optional<CacheOptions>         _cacheOptions;
-        optional<CachePolicy>          _cachePolicy;
-        optional<CoordinateSystemType> _cstype;
-        optional<std::string>          _referenceURI;
+        optional<std::string>            _name;
+        optional<ProfileOptions>         _profileOptions;
+        optional<CacheOptions>           _cacheOptions;
+        optional<CachePolicy>            _cachePolicy;
+        optional<CoordinateSystemType>   _cstype;
+        optional<std::string>            _referenceURI;
         optional<ElevationInterpolation> _elevationInterpolation;
+        optional<unsigned>               _elevTileSize;
     };
 }
 
diff --git a/src/osgEarth/MapOptions.cpp b/src/osgEarth/MapOptions.cpp
index e6cc2e1..86a07d9 100644
--- a/src/osgEarth/MapOptions.cpp
+++ b/src/osgEarth/MapOptions.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -45,7 +45,9 @@ MapOptions::fromConfig( const Config& conf )
     conf.getIfSet( "elevation_interpolation", "nearest",     _elevationInterpolation, INTERP_NEAREST);
     conf.getIfSet( "elevation_interpolation", "average",     _elevationInterpolation, INTERP_AVERAGE);
     conf.getIfSet( "elevation_interpolation", "bilinear",    _elevationInterpolation, INTERP_BILINEAR);
-    conf.getIfSet( "elevation_interpolation", "triangulate", _elevationInterpolation, INTERP_TRIANGULATE);    
+    conf.getIfSet( "elevation_interpolation", "triangulate", _elevationInterpolation, INTERP_TRIANGULATE);
+
+    conf.getIfSet( "elevation_tile_size", _elevTileSize );
 }
 
 Config
@@ -68,5 +70,7 @@ MapOptions::getConfig() const
     conf.updateIfSet( "elevation_interpolation", "bilinear",    _elevationInterpolation, INTERP_BILINEAR);
     conf.updateIfSet( "elevation_interpolation", "triangulate", _elevationInterpolation, INTERP_TRIANGULATE);
 
+    conf.updateIfSet( "elevation_tile_size", _elevTileSize );
+
     return conf;
 }
diff --git a/src/osgEarth/MaskLayer b/src/osgEarth/MaskLayer
index 9b7b1a4..bae409a 100644
--- a/src/osgEarth/MaskLayer
+++ b/src/osgEarth/MaskLayer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MaskLayer.cpp b/src/osgEarth/MaskLayer.cpp
index ca314ad..1466281 100644
--- a/src/osgEarth/MaskLayer.cpp
+++ b/src/osgEarth/MaskLayer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MaskNode b/src/osgEarth/MaskNode
index 9fba8c1..18d0e25 100644
--- a/src/osgEarth/MaskNode
+++ b/src/osgEarth/MaskNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MaskNode.cpp b/src/osgEarth/MaskNode.cpp
index 475f533..f67b644 100644
--- a/src/osgEarth/MaskNode.cpp
+++ b/src/osgEarth/MaskNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MaskSource b/src/osgEarth/MaskSource
index 4da3ed1..60dfeee 100644
--- a/src/osgEarth/MaskSource
+++ b/src/osgEarth/MaskSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MaskSource.cpp b/src/osgEarth/MaskSource.cpp
index c5d79ac..403711b 100644
--- a/src/osgEarth/MaskSource.cpp
+++ b/src/osgEarth/MaskSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Math b/src/osgEarth/Math
new file mode 100644
index 0000000..ac9f67c
--- /dev/null
+++ b/src/osgEarth/Math
@@ -0,0 +1,185 @@
+/* -*-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_MATH_H
+#define OSGEARTH_MATH_H 1
+
+#include <osgEarth/Common>
+#include <osg/Quat>
+#include <osg/Vec3>
+
+namespace osgEarth
+{
+    struct Line2d;
+    struct Segment2d;
+    struct Ray2d;
+    struct Segment3d;
+
+    /** Infinite 2D line. */
+    struct OSGEARTH_EXPORT Line2d
+    {
+        osg::Vec3d _a, _b; // two points on the line (Z ignored)
+        Line2d() { }
+        Line2d(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
+        Line2d(const osg::Vec2d& a, const osg::Vec2d& b) : _a(a.x(), a.y(), 0), _b(b.x(), b.y(), 0) { }
+        Line2d(const osg::Vec4d& a, const osg::Vec4d& b) : _a(a.x()/a.w(), a.y()/a.w(), a.z()/a.w()), _b(b.x()/b.w(), b.y()/b.w(), b.z()/b.w()) { }
+        Line2d(const Line2d& rhs) : _a(rhs._a), _b(rhs._b) { }
+        bool intersect( const Line2d&    rhs, osg::Vec2d& out ) const;
+        bool intersect( const Ray2d&     rhs, osg::Vec2d& out ) const;
+        bool intersect( const Segment2d& rhs, osg::Vec2d& out ) const;
+        bool intersect( const Line2d&    rhs, osg::Vec3d& out ) const;
+        bool intersect( const Ray2d&     rhs, osg::Vec3d& out ) const;
+        bool intersect( const Segment2d& rhs, osg::Vec3d& out ) const;
+        bool intersect( const Line2d&    rhs, osg::Vec4d& out ) const;
+        bool isPointOnLeft( const osg::Vec2d& p ) const;
+        bool isPointOnLeft( const osg::Vec3d& p ) const;
+    };
+    
+    //// rotate a Line
+    //inline Line2d operator* (const osg::Quat& q, const Line2d& rhs) {
+    //    return Line( q * rhs._a, q * rhs._b );
+    //}
+
+    /** Endpoint and a direction vector */
+    struct OSGEARTH_EXPORT Ray2d
+    {
+        osg::Vec3d _a;  // endpoint
+        osg::Vec3d _dv; // directional vector
+        Ray2d() { }
+        Ray2d(const osg::Vec3d& a, const osg::Vec3d& dv) : _a(a), _dv(dv) { }
+        Ray2d(const osg::Vec2d& a, const osg::Vec2d& dv) : _a(a.x(), a.y(), 0), _dv(dv.x(), dv.y(), 0) { }
+        Ray2d(const Ray2d& rhs) : _a(rhs._a), _dv(rhs._dv) { }
+        bool intersect( const Line2d&    rhs, osg::Vec2d& out ) const;
+        bool intersect( const Ray2d&     rhs, osg::Vec2d& out ) const;
+        bool intersect( const Segment2d& rhs, osg::Vec2d& out ) const;
+        bool intersect( const Line2d&    rhs, osg::Vec3d& out ) const;
+        bool intersect( const Ray2d&     rhs, osg::Vec3d& out ) const;
+        bool intersect( const Segment2d& rhs, osg::Vec3d& out ) const;
+        bool isPointOnLeft( const osg::Vec2d& p ) const;
+        bool isPointOnLeft( const osg::Vec3d& p ) const;
+        double angle(const Segment2d& rhs) const;
+    };
+    
+    //// rotate a Ray
+    //inline Ray operator* (const osg::Quat& q, const Ray& rhs) {
+    //    return Ray( q * rhs._a, q * rhs._dv );
+    //}
+
+    /** Finite line between two endpoints */
+    struct OSGEARTH_EXPORT Segment2d
+    {
+        osg::Vec3d _a, _b; // endpoints
+        Segment2d() { }
+        Segment2d(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
+        Segment2d(const Segment2d& rhs) : _a(rhs._a), _b(rhs._b) { }
+        bool intersect( const Line2d&    rhs, osg::Vec2d& out ) const;
+        bool intersect( const Ray2d&     rhs, osg::Vec2d& out ) const;
+        bool intersect( const Segment2d& rhs, osg::Vec2d& out ) const;
+        bool intersect( const Line2d&    rhs, osg::Vec3d& out ) const;
+        bool intersect( const Ray2d&     rhs, osg::Vec3d& out ) const;
+        bool intersect( const Segment2d& rhs, osg::Vec3d& out ) const;
+        bool isPointOnLeft( const osg::Vec2d& p ) const;
+        bool isPointOnLeft( const osg::Vec3d& p ) const;
+        Segment3d unrotateTo3D(const osg::Quat& q) const;
+        double angle(const Segment2d& rhs) const;
+    };
+
+    struct OSGEARTH_EXPORT Segment3d
+    {
+        osg::Vec3d _a, _b; // endpoints
+        Segment3d() { }
+        Segment3d(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
+        Segment3d(const Segment3d& rhs) : _a(rhs._a), _b(rhs._b) { }
+        Segment3d(const Segment2d& seg2d, const osg::Vec3& planeNormal);
+        Segment2d rotateTo2D(const osg::Quat& q) { return Segment2d(q*_a, q*_b); }
+    };
+    
+    //// rotate a Segment
+    //inline Segment operator* (const osg::Quat& q, const Segment& rhs) {
+    //    return Segment( q * rhs._a, q * rhs._b );
+    //}
+
+    /** 2D traingle with CCW winding. */
+    struct OSGEARTH_EXPORT Triangle2d
+    {
+        osg::Vec3d _a, _b, _c;
+        Triangle2d(const osg::Vec3d& a, const osg::Vec3d& b, const osg::Vec3d& c) : _a(a), _b(b), _c(c) { }
+        bool contains(const osg::Vec3d& p) const;
+    };
+
+#if 0
+    struct Line;
+    struct Segment;
+    struct Ray;
+
+    /** Infinite 3D line. */
+    struct Line
+    {
+        osg::Vec3d _a, _b; // two points on the line
+        Line(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
+        Line(const Line& rhs) : _a(rhs._a), _b(rhs._b) { }
+        bool intersectXY( const Line&    rhs, osg::Vec3d& out ) const;
+        bool intersectXY( const Ray&     rhs, osg::Vec3d& out ) const;
+        bool intersectXY( const Segment& rhs, osg::Vec3d& out ) const;
+    };
+    
+    // rotate a Line
+    inline Line operator* (const osg::Quat& q, const Line& rhs) {
+        return Line( q * rhs._a, q * rhs._b );
+    }
+
+    /** Endpoint and a direction vector */
+    struct Ray
+    {
+        osg::Vec3d _a;  // endpoint
+        osg::Vec3d _dv; // directional vector
+        Ray(const osg::Vec3d& a, const osg::Vec3d& dv) : _a(a), _dv(dv) { }
+        Ray(const Ray& rhs) : _a(rhs._a), _dv(rhs._dv) { }
+        bool intersectXY( const Line&    rhs, osg::Vec3d& out ) const;
+        bool intersectXY( const Ray&     rhs, osg::Vec3d& out ) const;
+        bool intersectXY( const Segment& rhs, osg::Vec3d& out ) const;
+        bool isPointOnLeftXY( const osg::Vec3d& p ) const;
+    };
+    
+    // rotate a Ray
+    inline Ray operator* (const osg::Quat& q, const Ray& rhs) {
+        return Ray( q * rhs._a, q * rhs._dv );
+    }
+
+    /** Finite line between two endpoints */
+    struct Segment
+    {
+        osg::Vec3d _a, _b; // endpoints
+        Segment(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
+        Segment(const Segment& rhs) : _a(rhs._a), _b(rhs._b) { }
+        bool intersectXY( const Line&    rhs, osg::Vec3d& out ) const;
+        bool intersectXY( const Ray&     rhs, osg::Vec3d& out ) const;
+        bool intersectXY( const Segment& rhs, osg::Vec3d& out ) const;
+        bool isPointOnLeftXY( const osg::Vec3d& p ) const;
+    };
+    
+    // rotate a Segment
+    inline Segment operator* (const osg::Quat& q, const Segment& rhs) {
+        return Segment( q * rhs._a, q * rhs._b );
+    }
+#endif
+
+};
+
+#endif
diff --git a/src/osgEarth/Math.cpp b/src/osgEarth/Math.cpp
new file mode 100644
index 0000000..fcecc75
--- /dev/null
+++ b/src/osgEarth/Math.cpp
@@ -0,0 +1,287 @@
+/* -*-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/Math>
+
+using namespace osgEarth;
+
+//------------------------------------------------------------------------
+
+namespace
+{
+    // ignores Z.
+    bool intersectRaysXY(const osg::Vec3d& p0, const osg::Vec3d& d0,
+                         const osg::Vec3d& p1, const osg::Vec3d& d1,
+                         osg::Vec3d& out_p,
+                         double&     out_u,
+                         double&     out_v)
+    {
+        static const double epsilon = 0.001;
+
+        double det = d0.y()*d1.x() - d0.x()*d1.y();
+        if ( osg::equivalent(det, 0.0, epsilon) )
+            return false; // parallel
+
+        out_u = (d1.x()*(p1.y()-p0.y())+d1.y()*(p0.x()-p1.x()))/det;
+        out_v = (d0.x()*(p1.y()-p0.y())+d0.y()*(p0.x()-p1.x()))/det;
+        out_p = p0 + d0*out_u;
+        return true;
+    }
+}
+
+bool
+Line2d::intersect( const Line2d& rhs, osg::Vec4d& out ) const
+{
+    double u, v;
+    osg::Vec3d temp;
+    bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), temp, u, v);
+    out.set( temp.x(), temp.y(), temp.z(), 1.0 );
+    return ok;
+}
+
+bool
+Line2d::intersect( const Line2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    return intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), out, u, v);
+}
+
+bool
+Line2d::intersect( const Segment2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), out, u, v);
+    return ok && v >= 0.0 && v <= 1.0;
+}
+
+bool
+Line2d::intersect( const Ray2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, rhs._dv, out, u, v);
+    return ok && v >= 0.0;
+}
+
+bool
+Line2d::intersect( const Line2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Line2d::intersect( const Segment2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Line2d::intersect( const Ray2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Line2d::isPointOnLeft( const osg::Vec3d& p ) const
+{
+    return ((_b-_a) ^ (p-_a)).z() >= 0.0;
+}
+
+bool
+Line2d::isPointOnLeft( const osg::Vec2d& p ) const
+{
+    return ((_b-_a) ^ (osg::Vec3d(p.x(),p.y(),0)-_a)).z() >= 0.0;
+}
+
+//--------------------------------------------------------------------------
+
+bool
+Ray2d::intersect( const Line2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, _dv, rhs._a, (rhs._b-rhs._a), out, u, v);
+    return ok && u >= 0.0;
+}
+
+bool
+Ray2d::intersect( const Segment2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, _dv, rhs._a, (rhs._b-rhs._a), out, u, v);
+    return ok && u >= 0.0 && v >= 0.0 && v <= 1.0;
+}
+
+bool
+Ray2d::intersect( const Ray2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, _dv, rhs._a, rhs._dv, out, u, v);
+    return ok && u >= 0.0 && v >= 0.0;
+}
+
+bool
+Ray2d::intersect( const Line2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Ray2d::intersect( const Segment2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Ray2d::intersect( const Ray2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Ray2d::isPointOnLeft( const osg::Vec3d& p ) const
+{
+    return (_dv ^ (p-_a)).z() >= 0.0;
+}
+
+bool
+Ray2d::isPointOnLeft( const osg::Vec2d& p ) const
+{
+    return (_dv ^ (osg::Vec3d(p.x(),p.y(),0)-_a)).z() >= 0.0;
+}
+
+double
+Ray2d::angle(const Segment2d& rhs) const
+{
+    osg::Vec2d v0( _dv.x(), _dv.y() );
+    v0.normalize();
+    osg::Vec2d v1(rhs._b.x()-rhs._a.x(), rhs._b.y()-rhs._a.y()); 
+    v1.normalize();
+    return acos( v0 * v1 );
+}
+
+//--------------------------------------------------------------------------
+
+bool
+Segment2d::intersect( const Line2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), out, u, v);
+    return ok && u >= 0.0 && u <= 1.0;
+}
+
+bool
+Segment2d::intersect( const Segment2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), out, u, v);
+    return ok && u >= 0.0 && u <= 1.0 && v >= 0.0 && v <= 1.0;
+}
+
+bool
+Segment2d::intersect( const Ray2d& rhs, osg::Vec3d& out ) const
+{
+    double u, v;
+    bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, rhs._dv, out, u, v);
+    return ok && u >= 0.0 && u <= 1.0 && v >= 0.0;
+}
+
+bool
+Segment2d::intersect( const Line2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Segment2d::intersect( const Segment2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Segment2d::intersect( const Ray2d& rhs, osg::Vec2d& out ) const
+{
+    osg::Vec3d out3;
+    bool ok = intersect( rhs, out3 );
+    out.set(out3.x(), out3.y());
+    return ok;
+}
+
+bool
+Segment2d::isPointOnLeft( const osg::Vec3d& p ) const
+{
+    return ((_b-_a) ^ (p-_a)).z() >= 0.0;
+}
+
+bool
+Segment2d::isPointOnLeft( const osg::Vec2d& p ) const
+{
+    return ((_b-_a) ^ (osg::Vec3d(p.x(),p.y(),0)-_a)).z() >= 0.0;
+}
+
+double
+Segment2d::angle(const Segment2d& rhs) const
+{
+    osg::Vec2d v0(_b.x()-_a.x(), _b.y()-_a.y());
+    v0.normalize();
+    osg::Vec2d v1(rhs._b.x()-rhs._a.x(), rhs._b.y()-rhs._a.y());
+    v1.normalize();
+    return acos( v0 * v1 );
+}
+
+//--------------------------------------------------------------------------
+
+Segment3d
+Segment2d::unrotateTo3D(const osg::Quat& q) const
+{
+    osg::Quat qi = q.inverse();
+    return Segment3d( qi*_a, qi*_b );
+}
+
+//--------------------------------------------------------------------------
+
+bool
+Triangle2d::contains(const osg::Vec3d& p) const
+{
+    if ( !Line2d(_a, _b).isPointOnLeft(p) ) return false;
+    if ( !Line2d(_b, _c).isPointOnLeft(p) ) return false;
+    if ( !Line2d(_c, _a).isPointOnLeft(p) ) return false;
+    return true;
+}
diff --git a/src/osgEarth/MemCache b/src/osgEarth/MemCache
index 4644558..83e972a 100644
--- a/src/osgEarth/MemCache
+++ b/src/osgEarth/MemCache
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/MemCache.cpp b/src/osgEarth/MemCache.cpp
index 49ac494..4b7551d 100644
--- a/src/osgEarth/MemCache.cpp
+++ b/src/osgEarth/MemCache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -42,7 +42,8 @@ namespace
         ReadResult readObject(const std::string& key,
                               double             maxAge )
         {
-            MemCacheLRU::Record rec = _lru.get(key);
+            MemCacheLRU::Record rec;
+            _lru.get(key, rec);
 
             // clone required since the cache is in memory
 
diff --git a/src/osgEarth/MimeTypes.cpp b/src/osgEarth/MimeTypes.cpp
index 397d855..6b6ede8 100644
--- a/src/osgEarth/MimeTypes.cpp
+++ b/src/osgEarth/MimeTypes.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ModelLayer b/src/osgEarth/ModelLayer
index be797d1..eb0801d 100644
--- a/src/osgEarth/ModelLayer
+++ b/src/osgEarth/ModelLayer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ModelLayer.cpp b/src/osgEarth/ModelLayer.cpp
index fc798e7..b7d8d0a 100644
--- a/src/osgEarth/ModelLayer.cpp
+++ b/src/osgEarth/ModelLayer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@ namespace
     {
         NodeModelSource( osg::Node* node ) : _node(node) { }
 
-        osg::Node* createNode(const Map* map, const osgDB::Options* dbOptions, ProgressCallback* progress) {
+        osg::Node* createNodeImplementation(const Map* map, const osgDB::Options* dbOptions, ProgressCallback* progress) {
             return _node.get();
         }
 
diff --git a/src/osgEarth/ModelSource b/src/osgEarth/ModelSource
index 52a5d30..3b2d0f5 100644
--- a/src/osgEarth/ModelSource
+++ b/src/osgEarth/ModelSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -88,11 +88,16 @@ namespace osgEarth
         /** dtor */
         virtual ~ModelSource() { }
 
+        osg::Node* createNode(
+            const Map*            map,
+            const osgDB::Options* dbOptions,
+            ProgressCallback*     progress );
+
         /**
          * Subclass implements this method to create a scene graph within the
          * context of the provided Map.
          */
-        virtual osg::Node* createNode(
+        virtual osg::Node* createNodeImplementation(
             const Map*            map,
             const osgDB::Options* dbOptions,
             ProgressCallback*     progress ) =0;
diff --git a/src/osgEarth/ModelSource.cpp b/src/osgEarth/ModelSource.cpp
index ddb958f..7b35ef1 100644
--- a/src/osgEarth/ModelSource.cpp
+++ b/src/osgEarth/ModelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -75,6 +75,20 @@ _options( options )
 }
 
 
+osg::Node* 
+ModelSource::createNode(const Map*            map,
+                        const osgDB::Options* dbOptions,
+                        ProgressCallback*     progress )
+{
+    osg::Node* node = createNodeImplementation(map, dbOptions, progress);
+    if ( node )
+    {
+        firePostProcessors( node );
+    }
+    return node;
+}
+
+
 void 
 ModelSource::addPostProcessor( NodeOperation* op )
 {
diff --git a/src/osgEarth/NodeUtils b/src/osgEarth/NodeUtils
index da03699..6e9e2dc 100644
--- a/src/osgEarth/NodeUtils
+++ b/src/osgEarth/NodeUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -228,6 +228,35 @@ namespace osgEarth
     }
 
     /**
+     * Replace one group with another
+     */
+    inline void replaceGroup( osg::Group* oldGroup, osg::Group* newGroup )
+    {
+        if ( oldGroup && newGroup && oldGroup->getNumParents() > 0 )
+        {
+            for(unsigned i=0; i<oldGroup->getNumChildren(); ++i)
+            {
+                newGroup->addChild( oldGroup->getChild(0) );
+            }
+
+            osg::Node::ParentList parents = oldGroup->getParents();
+            for(osg::Node::ParentList::iterator i = parents.begin(); i != parents.end(); ++i )
+            {
+                (*i)->replaceChild( oldGroup, newGroup );
+            }
+        }
+    }
+
+    /**
+     * Remove all a group's children.
+     */
+    inline void clearChildren( osg::Group* group )
+    {
+        if ( group )
+            group->removeChildren( 0, group->getNumChildren() );
+    }
+
+    /**
      * OSG Group that keeps its children as observer_ptrs instead of ref_ptrs, and
      * removes them when they deref.
      */
@@ -239,6 +268,44 @@ namespace osgEarth
         std::set<osg::Node*> _orphans;
     };
 
+    
+    /**
+     * Group that acts like a normal group but also notifies another
+     * object when a change occurs.
+     */
+    template<typename T>
+    class NotifierGroup : public osg::Group
+    {
+    public:
+        NotifierGroup(T* listener) : _listener(listener) { }
+
+        virtual bool addChild( osg::Node* child ) {
+            bool ok = osg::Group::addChild(child);
+            if ( ok && _listener.valid() ) _listener->onGroupChanged(this);
+            return ok;
+        }
+        virtual bool insertChild( unsigned index, osg::Node* child ) {
+            bool ok = osg::Group::insertChild(index, child);
+            if ( ok && _listener.valid() ) _listener->onGroupChanged(this);
+            return ok;
+        }
+        virtual bool removeChild( osg::Node* child ) {
+            bool ok = osg::Group::removeChild( child );
+            if ( ok && _listener.valid() ) _listener->onGroupChanged(this);
+            return ok;
+        }
+        virtual bool replaceChild( osg::Node* origChild, osg::Node* newChild ) {
+            bool ok = osg::Group::replaceChild(origChild, newChild);
+            if ( ok && _listener.valid() ) _listener->onGroupChanged(this);
+            return ok;
+        }
+
+    protected:
+        virtual ~NotifierGroup() { }
+        osg::observer_ptr<T> _listener;
+    };
+
+
     /**
      * Adjusts a node's update traversal count by a delta.
      * Only safe to call from the UPDATE thread
diff --git a/src/osgEarth/NodeUtils.cpp b/src/osgEarth/NodeUtils.cpp
index 0a9d002..a4c0487 100644
--- a/src/osgEarth/NodeUtils.cpp
+++ b/src/osgEarth/NodeUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -114,7 +114,7 @@ RemoveEmptyGroupsVisitor::apply( osg::Group& group )
             osg::Group* child = group.getChild(i)->asGroup();
             if ( child )
             {
-                if (child->className() == std::string("Group") &&
+                if (std::string(child->className()).compare("Group") == 0 &&
                     child->getStateSet() == 0L            &&
                     child->getCullCallback() == 0L        &&
                     child->getUpdateCallback() == 0L      &&
diff --git a/src/osgEarth/Notify b/src/osgEarth/Notify
index d256611..574cb70 100644
--- a/src/osgEarth/Notify
+++ b/src/osgEarth/Notify
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -50,6 +50,7 @@ namespace osgEarth
 #define OE_WARN OE_NOTIFY(osg::WARN,"[osgEarth]* ")
 #define OE_NOTICE OE_NOTIFY(osg::NOTICE,"[osgEarth]  ")
 #define OE_INFO OE_NOTIFY(osg::INFO,"[osgEarth]  ")
+#define OE_INFO_CONTINUE OE_NOTIFY(osg::INFO, "")
 #define OE_DEBUG OE_NOTIFY(osg::DEBUG_INFO,"[osgEarth]  ")
 #define OE_NULL if(false) osgEarth::notify(osg::ALWAYS)
 
diff --git a/src/osgEarth/Notify.cpp b/src/osgEarth/Notify.cpp
index 7197839..8127756 100644
--- a/src/osgEarth/Notify.cpp
+++ b/src/osgEarth/Notify.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/OverlayDecorator b/src/osgEarth/OverlayDecorator
index c0b7023..006a10d 100644
--- a/src/osgEarth/OverlayDecorator
+++ b/src/osgEarth/OverlayDecorator
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -31,88 +31,44 @@
 namespace osgEarth
 {
     class TerrainEngineNode;
+    class OverlayDecorator;
+    class OverlayTechnique;
 
     /**
-     * Projects an overlay scene graph onto the main model.
-     *
-     * The OverlayDecorator is automatically installed in the MapNode and is used
-     * to display ModelLayer's whose "overlay" property is set to true.
-     *
-     * This class is similar in scope to osgSim::OverlayNode, but is optimized
-     * for use with osgEarth and geocentric terrains.
+     * Overlays geometry onto the terrain using various techniques.
      */
     class OSGEARTH_EXPORT OverlayDecorator : public TerrainDecorator
     {
     public:
         OverlayDecorator();
 
-        /** dtor */
-        virtual ~OverlayDecorator() { }
-
         /**
-         * The scene graph to be sampled an overlaid on the terrain.
+         * Adds a new overlay technique to the decorator.
          */
-        void setOverlayGraph( osg::Node* node );
-        osg::Node* getOverlayGraph() const { return _overlayGraph.get(); }
+        void addTechnique( OverlayTechnique* technique );
 
         /**
-         * Explicity sets the texture unit to use. Note! When you add this class
-         * to a MapNode, it will automatically allocate a free texture unit; so you
-         * usually do NOT need to call this method.
+         * Gets the group for nodes that are to be overlaid with
+         * the specified Technique.
          */
-        void setTextureUnit( int unit );
-        int getTextureUnit() const { return *_textureUnit; }
+        template<typename T>
+        osg::Group* getGroup() {
+            for(unsigned i=0; i<_techniques.size(); ++i ) {
+                if ( dynamic_cast<T*>(_techniques[i].get()) )
+                    return _overlayGroups[i].get();
+            }
+            return 0L;
+        }
 
         /**
-         * The size (resolution in both directions) of the overlay texture. By
-         * default, this defaults to 4096 or your hardware's maximum supported
-         * texture size, whichever is less.
+         * The traversal mask to use when traversing the overlay groups.
          */
-        void setTextureSize( int texSize );
-        int getTextureSize() const { return *_textureSize; }
-
-        /**
-         * Whether mipmapping is enabled on the projected overlay texture. Mapmapping
-         * will improve the visual appearance, but will use more memory, and will affect
-         * performance for overlays that are dynamic. Mipmapping can slow things down
-         * a LOT on some GPUs (e.g. Intel GMA)
-         */
-        void setMipMapping( bool value );
-        bool getMipMapping() const { return _mipmapping; }
+        void setOverlayGraphTraversalMask( unsigned mask );
 
-        /**
-         * Gets the maximum horizon distance
-         */
         double getMaxHorizonDistance() const;
-
-        /**
-         * Sets the maximum horizon distance.  The computed far distance will be no longer than this value.
-         * If you use overlay data that spans large distances it can become pixelated when you tilt your camera to look out over
-         * the horizon b/c the view frustum can extend over long distances.  If you set this to a smaller value you can control
-         * how far out you want to go and reduce the pixelation.  However, if you set this value to be too small, features at a distance may
-         * be clipped out of the scene.
-         */
-        void setMaxHorizonDistance( double maxHorizonDistance );
-
-        /**
-         * Whether to enable blending on the RTT camera graph. Default = true. You might
-         * want to disable this is you are draping polygons that cover a very large area
-         * and curve around the globe; sometimes they suffer from tessellation artifacts
-         * when draped.
-         */
-        void setOverlayBlending( bool value );
-        bool getOverlayBlending() const { return _rttBlending; }
+        void setMaxHorizonDistance( double horizonDistance );
 
 
-        /**
-         * 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
         virtual void onInstall( TerrainEngineNode* engine );
         virtual void onUninstall( TerrainEngineNode* engine );
@@ -120,9 +76,51 @@ namespace osgEarth
     public: // osg::Node
         void traverse( osg::NodeVisitor& nv );
 
+    public: // NotiferGroup listener interface
+        void onGroupChanged(osg::Group* group);
+
+    protected:
+        virtual ~OverlayDecorator() { }
+
+        //// Root group for an overlay technique
+        //struct OverlayRootGroup : public osg::Group
+        //{
+        //    OverlayRootGroup();
+        //    osg::Polytope& cullingFrustum() { return _cb->_cullingFrustum; }
+        //    osg::ref_ptr<osg::NodeCallback> _cb;
+        //    mutable Threading::Mutex        _mutex;
+        //    osg::BoundingSphere computeBound() const; // override
+        //};
+
+    public:
+
+        // RTT camera parameters for an overlay technique. There will be
+        // one of these per technique, per view.
+        struct TechRTTParams
+        {
+            osg::Camera*                  _mainCamera;       // Camera to which this per-view data is attached
+            osg::ref_ptr<osg::Camera>     _rttCamera;        // RTT camera.
+            osg::Matrixd                  _rttViewMatrix;    // View matrix of RTT camera
+            osg::Matrixd                  _rttProjMatrix;    // Projection matrix of RTT camera
+            osg::Group*                   _group;            // contains the overlay graphics
+            osg::StateSet*                _terrainStateSet;  // same object as in PerViewData (shared across techniques)
+            osg::ref_ptr<osg::Referenced> _techniqueData;    // technique sets this if needed
+            const double*                 _horizonDistance;  // points to the PVD horizon distance.
+            osg::Group*                   _terrainParent;    // the terrain is in getChild(0).
+        };
+
+        // One of these per view (camera). The terrain state set
+        // if Shared almong all the techniques under a view.
+        struct PerViewData
+        {
+            osg::Camera*                _camera;                // Camera to which this per-view data is attached
+            std::vector<TechRTTParams>  _techParams;            // Pre-view data for each technique
+            osg::ref_ptr<osg::StateSet> _sharedTerrainStateSet; // shared state set to apply to the terrain traversal
+            double                      _sharedHorizonDistance; // horizon distnace (not used?)
+            osg::Matrix                 _prevViewMatrix;        // last frame's view matrix
+        };
+
     private:
-        osg::ref_ptr<osg::Node>       _overlayGraph;
-        osg::ref_ptr<osg::Uniform>    _warpUniform;
         optional<int>                 _explicitTextureUnit;
         optional<int>                 _textureUnit;
         optional<int>                 _textureSize;
@@ -136,36 +134,31 @@ namespace osgEarth
         
         double                        _maxHorizonDistance;
         bool                          _attachStencil;
+        
+        unsigned                      _totalOverlayChildren;
 
         osg::ref_ptr<const SpatialReference>    _srs;
         osg::ref_ptr<const osg::EllipsoidModel> _ellipsoid;
         osg::observer_ptr<TerrainEngineNode>    _engine;
-        
-        struct PerViewData
-        {
-            osg::ref_ptr<osg::Camera>     _rttCamera;
-            osg::ref_ptr<osg::Uniform>    _texGenUniform;
-            osg::ref_ptr<osg::TexGen>     _texGen;
-            osg::Matrixd                  _rttViewMatrix;
-            osg::Matrixd                  _rttProjMatrix;
-            osg::ref_ptr<osg::StateSet>   _subgraphStateSet;
-        };
 
+        // Mapping of each RTT camera params vector to a Camera (per view data)
         typedef std::map<osg::Camera*, PerViewData> PerViewDataMap;
-        //typedef std::map<osg::NodeVisitor*, PerViewData> PerViewDataMap;
+
         PerViewDataMap _perViewData;
         Threading::ReadWriteMutex _perViewDataMutex;
 
+        typedef std::vector< osg::ref_ptr<OverlayTechnique> > Techniques;
+        Techniques _techniques;
+        Techniques _unsupportedTechniques;
+
+        typedef std::vector< osg::ref_ptr<osg::Group> > Groups;
+        Groups _overlayGroups;
+
 
     private:
-        void updateRTTCamera(PerViewData& pvd);
-        void cull( osgUtil::CullVisitor* cv, PerViewData& pvd );
-        void initializeForOverlayGraph();
-        void initializePerViewData( PerViewData& );
-        void initSubgraphShaders( PerViewData& );
-        bool checkNeedsUpdate( PerViewData& );
+        void cullTerrainAndCalculateRTTParams( osgUtil::CullVisitor* cv, PerViewData& pvd );
+        void initializePerViewData( PerViewData&, osg::Camera* );
         PerViewData& getPerViewData( osg::Camera* key );
-        //PerViewData& getPerViewData( osg::NodeVisitor* key );
 
     public:
         // marker class for DrapeableNode support.
@@ -181,6 +174,38 @@ namespace osgEarth
         bool _dumpRequested;
     };
 
+
+    /**
+     * Abstract interface for an overlay technique implementation
+     */
+    class OverlayTechnique : public osg::Referenced
+    {
+    protected:
+        bool _supported;
+        OverlayTechnique() : _supported(true) { }
+        virtual ~OverlayTechnique() { }
+
+    public:
+        virtual bool supported() { return _supported; }
+
+        virtual bool hasData(
+            OverlayDecorator::TechRTTParams& params) const { return true; }
+
+        virtual void onInstall( TerrainEngineNode* engine ) { }
+
+        virtual void onUninstall( TerrainEngineNode* engine ) { }
+
+        virtual void reestablish( TerrainEngineNode* engine ) { }
+
+        virtual void preCullTerrain(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv ) { }
+
+        virtual void cullOverlayGroup(
+            OverlayDecorator::TechRTTParams& params,
+            osgUtil::CullVisitor*            cv ) { }
+    };
+
 } // namespace osgEarth
 
 #endif //OSGEARTH_OVERLAY_DECORATOR
diff --git a/src/osgEarth/OverlayDecorator.cpp b/src/osgEarth/OverlayDecorator.cpp
old mode 100644
new mode 100755
index 1c4759f..f86b408
--- a/src/osgEarth/OverlayDecorator.cpp
+++ b/src/osgEarth/OverlayDecorator.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -17,21 +17,19 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include <osgEarth/OverlayDecorator>
+#include <osgEarth/DrapingTechnique>
 #include <osgEarth/MapInfo>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Registry>
-#include <osgEarth/TextureCompositor>
-#include <osgEarth/VirtualProgram>
 #include <osgEarth/Capabilities>
-#include <osg/Texture2D>
-#include <osg/TexEnv>
-#include <osg/BlendFunc>
-#include <osg/ShapeDrawable>
+#include <osgEarth/CullingUtils>
+
 #include <osg/AutoTransform>
 #include <osg/ComputeBoundsVisitor>
-#include <osgGA/EventVisitor>
+#include <osg/ShapeDrawable>
 #include <osgShadow/ConvexPolyhedron>
 #include <osgUtil/LineSegmentIntersector>
+
 #include <iomanip>
 #include <stack>
 
@@ -40,405 +38,263 @@
 //#define OE_TEST if (_dumpRequested) OE_INFO << std::setprecision(9)
 #define OE_TEST OE_NULL
 
+
 using namespace osgEarth;
 
 //---------------------------------------------------------------------------
 
 namespace
 {
-    /**
-     * 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
-     * look vector.
-     *
-     * IOW, all the test points are "projected" on to the plane defined by the camera point
-     * and the look (normal) vector, and then the distances from the camera point to each
-     * projected point are tested in order to find the min/max extent.
-     */
-    void
-    getMinMaxExtentInSilhouette(const osg::Vec3d& cam, const osg::Vec3d& look, 
-                                std::vector<osg::Vec3d>& verts,
-                                double& out_eMin, double& out_eMax )
+    void setFar(osg::Matrix& m, double newFar)
     {
-        double minSqrDist2D = DBL_MAX;
-        double maxSqrDist2D = -DBL_MAX;
-        osg::Plane plane( look, cam );
-
-        for( std::vector<osg::Vec3d>::iterator i = verts.begin(); i != verts.end(); ++i )
+        if ( osg::equivalent(m(0,3),0.0) && osg::equivalent(m(1,3),0.0) && osg::equivalent(m(2,3),0.0) )
         {
-            osg::Vec3d& point = *i;
-
-            // project the vert onto the camera plane:
-            double signedDist = plane.distance( point );
-            point += (-plane.getNormal() * signedDist);
-            
-            // then calculate the 2D distance to the camera:
-            double sqrDist2D = (cam-point).length2();
-            if ( sqrDist2D > maxSqrDist2D )
-                maxSqrDist2D = sqrDist2D;
-            if ( sqrDist2D < minSqrDist2D )
-                minSqrDist2D = sqrDist2D;
+            double l,r,b,t,n,f;
+            m.getOrtho(l,r,b,t,n,f);
+            m.makeOrtho(l,r,b,t,n,newFar);
+        }
+        else
+        {
+            double v,a,n,f;
+            m.getPerspective(v,a,n,f);
+            m.makePerspective(v,a,n,newFar);
         }
-
-        out_eMin = sqrt( minSqrDist2D );
-        out_eMax = sqrt( maxSqrDist2D );
     }
-    
+
+
     /**
-     * Same as the method above, but extracts the verts from a bounding box.
+     * Interects a finite ray with a sphere of radius R. The ray is defined
+     * by the X and Y components (in projection aka "clip" space). The function
+     * uses this information to build a ray from Z=-1 to Z=1. 
+     *
+     * Places the intersection point(s) in the output vector.
      */
-    void
-    getMinMaxExtentInSilhouette(const osg::Vec3d& cam, const osg::Vec3d& look, 
-                                const osg::BoundingBox& bbox,
-                                double& out_eMin, double& out_eMax )
+    bool
+    intersectClipRayWithSphere(double                   clipx, 
+                               double                   clipy, 
+                               const osg::Matrix&       clipToWorld, 
+                               double                   R,
+                               double&                  inout_maxDist2)
     {
-        std::vector<osg::Vec3d> verts(8);
-        verts[0].set( bbox.xMin(), bbox.yMin(), bbox.zMin() );
-        verts[1].set( bbox.xMin(), bbox.yMin(), bbox.zMax() );
-        verts[2].set( bbox.xMin(), bbox.yMax(), bbox.zMin() );
-        verts[3].set( bbox.xMin(), bbox.yMax(), bbox.zMax() );
-        verts[4].set( bbox.xMax(), bbox.yMin(), bbox.zMin() );
-        verts[5].set( bbox.xMax(), bbox.yMin(), bbox.zMax() );
-        verts[6].set( bbox.xMax(), bbox.yMax(), bbox.zMin() );
-        verts[7].set( bbox.xMax(), bbox.yMax(), bbox.zMax() );
-        getMinMaxExtentInSilhouette( cam, look, verts, out_eMin, out_eMax );
-    }
-}
+        double dist2 = 0.0;
 
-//---------------------------------------------------------------------------
+        osg::Vec3d p0 = osg::Vec3d(clipx, clipy, -1.0) * clipToWorld; // near plane
+        osg::Vec3d p1 = osg::Vec3d(clipx, clipy,  1.0) * clipToWorld; // far plane
 
-OverlayDecorator::OverlayDecorator() :
-_textureUnit     ( 1 ),
-_textureSize     ( 1024 ),
-_useShaders      ( false ),
-_mipmapping      ( false ),
-_rttBlending     ( true ),
-_updatePending   ( false ),
-_dumpRequested   ( false ),
-_rttTraversalMask( ~0 ),
-_maxHorizonDistance( DBL_MAX ),
-_attachStencil   ( true )
-{
-    // nop
-}
+        // http://stackoverflow.com/questions/6533856/ray-sphere-intersection
 
-void
-OverlayDecorator::initializeForOverlayGraph()
-{
-    if ( !_engine.valid() ) return;
+        osg::Vec3d d = p1-p0;
 
-    if ( _overlayGraph.valid() )
-    {
-        // apply the user-request texture unit, if applicable:
-        if ( _explicitTextureUnit.isSet() )
-        {
-            if ( !_textureUnit.isSet() || *_textureUnit != *_explicitTextureUnit )
-            {
-                _textureUnit = *_explicitTextureUnit;
-            }
-        }
+        double A = d * d;
+        double B = 2.0 * (d * p0);
+        double C = (p0 * p0) - R*R;
 
-        // otherwise, automatically allocate a texture unit if necessary:
-        else if ( !_textureUnit.isSet() ) //&& _useShaders )
+        // now solve the quadratic A + B*t + C*t^2 = 0.
+        double D = B*B - 4.0*A*C;
+        if ( D >= 0 )
         {
-            int texUnit;
-            if ( _engine->getTextureCompositor()->reserveTextureImageUnit( texUnit ) )
+            if ( osg::equivalent(D, 0.0) )
             {
-                _textureUnit = texUnit;
-                OE_INFO << LC << "Reserved texture image unit " << *_textureUnit << std::endl;
+                // one root (line is tangent to sphere)
+                double t = -B/(2.0*A);
+                if (t >= 0.0)
+                {
+                    osg::Vec3d v = d*t;
+                    dist2 = v.length2();
+                }
             }
             else
             {
-                OE_WARN << LC << "Uh oh, no texture image units available." << std::endl;
+                // two roots (line passes through sphere twice)
+                // find the closer of the two.
+                double sqrtD = sqrt(D);
+                double t0 = (-B + sqrtD)/(2.0*A);
+                double t1 = (-B - sqrtD)/(2.0*A);
+
+                if ( t0 >= 0.0 && t1 >= 0.0 )
+                {
+                    osg::Vec3d v = d*std::min(t0,t1);
+                    dist2 = v.length2();
+                }
+                else if ( t0 >= 0.0 )
+                {
+                    osg::Vec3d v = d*t0;
+                    dist2 = v.length2();
+                }
+                else if ( t1 >= 0.0 )
+                {
+                    osg::Vec3d v = d*t1;
+                    dist2 = v.length2();
+                }
             }
         }
-    }
-}
-
 
-void
-OverlayDecorator::initializePerViewData( PerViewData& pvd )
-{
-    if ( !_textureUnit.isSet() || !_overlayGraph.valid() )
-        return;
-
-    // create the projected texture:
-    osg::Texture2D* projTexture = new osg::Texture2D();
-    projTexture->setTextureSize( *_textureSize, *_textureSize );
-    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_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:
-    pvd._rttCamera = new osg::Camera();
-    pvd._rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
-    pvd._rttCamera->setClearStencil( 0 );
-    pvd._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
-    // this ref frame causes the RTT to inherit its viewpoint from above (in order to properly
-    // process PagedLOD's etc. -- it doesn't affect the perspective of the RTT camera though)
-    pvd._rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
-    pvd._rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize );
-    pvd._rttCamera->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
-    pvd._rttCamera->setRenderOrder( osg::Camera::PRE_RENDER );
-    pvd._rttCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
-
-    pvd._rttCamera->attach( osg::Camera::COLOR_BUFFER, projTexture, 0, 0, _mipmapping );
-
-    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() )
+        if ( dist2 > inout_maxDist2 )
         {
-#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
+            inout_maxDist2 = dist2;
+            return true;
         }
         else
         {
-            pvd._rttCamera->attach( osg::Camera::STENCIL_BUFFER, GL_STENCIL_INDEX );
+            // either no intersection, or the distance was not the max.
+            return false;
         }
     }
 
-    osg::StateSet* rttStateSet = pvd._rttCamera->getOrCreateStateSet();
+    /**
+     * Same as above, but intersects the ray with a static 2D plane
+     * (for use in projected map mode)
+     */
+    void
+    intersectClipRayWithPlane(double                   clipx, 
+                              double                   clipy, 
+                              const osg::Matrix&       clipToWorld,
+                              double&                  inout_maxDist2)
+    {
+        osg::Vec3d p0 = osg::Vec3d(clipx, clipy, -1.0) * clipToWorld; // near plane
+        osg::Vec3d p1 = osg::Vec3d(clipx, clipy,  1.0) * clipToWorld; // far plane
+
+        // zero-level plane is hard-coded here
+        osg::Vec3d planePoint(0,0,0);
+        osg::Vec3d planeNormal(0,0,1);
 
-    rttStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
+        osg::Vec3d L = p1-p0;
+        L.normalize();
 
-    // install a new default shader program that replaces anything from above.
-    if ( _useShaders )
-    {
-        VirtualProgram* vp = new VirtualProgram();
-        vp->setName( "overlay rtt" );
-        vp->installDefaultColoringAndLightingShaders();
-        vp->setInheritShaders( false );
-        rttStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
-    }
-    
-    if ( _rttBlending )
-    {
-        //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;        
-        if (Registry::instance()->getCapabilities().supportsGLSL(1.4f))
-        {
-            //Blend Func Separate is only available on OpenGL 1.4 and above
-            blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        }
-        else
+        double denom = L * planeNormal;
+        if ( !osg::equivalent(denom, 0.0) )
         {
-            blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+            double d = ((planePoint - p0) * planeNormal) / denom;
+            if ( d > 0.0 )
+                inout_maxDist2 = (L*d).length2();
         }
-
-        rttStateSet->setAttributeAndModes(blendFunc, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
-    }
-    else
-    {
-        rttStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
     }
 
-    // attach the overlay graph to the RTT camera.
-    if ( _overlayGraph.valid() && ( _overlayGraph->getNumParents() == 0 || _overlayGraph->getParent(0) != pvd._rttCamera.get() ))
-    {
-        if ( pvd._rttCamera->getNumChildren() > 0 )
-            pvd._rttCamera->replaceChild( 0, _overlayGraph.get() );
-        else
-            pvd._rttCamera->addChild( _overlayGraph.get() );
-    }
-
-    // overlay geometry is rendered with no depth testing, and in the order it's found in the
-    // scene graph... until further notice...
-    rttStateSet->setMode(GL_DEPTH_TEST, 0);
-    rttStateSet->setBinName( "TraversalOrderBin" );
 
+    /**
+     * Takes a set of world verts and finds their X-Y bounding box in the 
+     * plane of the camera represented by the specified view matrix. Also
+     * calculates the maximum distance from the eyepoint to a vertex (3D).
+     */
+    void
+    getExtentInSilhouette(const osg::Matrix& viewMatrix,
+                          const osg::Vec3d& eye,
+                          std::vector<osg::Vec3d>& verts,
+                          double& xmin, double& ymin,
+                          double& xmax, double& ymax,
+                          double& maxDistance)
+    {
+        xmin = DBL_MAX, ymin = DBL_MAX, xmax = -DBL_MAX, ymax = -DBL_MAX;
+        double maxDist2 = 0.0;
 
-    // assemble the subgraph stateset:
-    pvd._subgraphStateSet = new osg::StateSet();
+        for( std::vector<osg::Vec3d>::iterator i = verts.begin(); i != verts.end(); ++i )
+        {
+            osg::Vec3d d = (*i) * viewMatrix; // world to view
+            if ( d.x() < xmin ) xmin = d.x();
+            if ( d.x() > xmax ) xmax = d.x();
+            if ( d.y() < ymin ) ymin = d.y();
+            if ( d.y() > ymax ) ymax = d.y();
+            
+            double dist2 = ((*i)-eye).length2();
+            if ( dist2 > maxDist2 )
+                maxDist2 = dist2;
+        }
 
-    pvd._subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, projTexture, osg::StateAttribute::ON );
-    
-    if ( _useShaders )
-    {            
-        // GPU path
-        initSubgraphShaders( pvd );
-    }
-    else
-    {
-        // 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 );
+        maxDistance = sqrt(maxDist2);
     }
 }
 
-void
-OverlayDecorator::initSubgraphShaders( PerViewData& pvd )
-{
-    osg::StateSet* set = pvd._subgraphStateSet.get();
-
-    VirtualProgram* vp = new VirtualProgram();
-    vp->setName( "OverlayDecorator subgraph shader" );
-    set->setAttributeAndModes( vp, osg::StateAttribute::ON );
-
-    // sampler for projected texture:
-    set->getOrCreateUniform( "oe_overlay_ProjTex", osg::Uniform::SAMPLER_2D )->set( *_textureUnit );
-
-    // the texture projection matrix uniform.
-    pvd._texGenUniform = set->getOrCreateUniform( "oe_overlay_TexGenMatrix", osg::Uniform::FLOAT_MAT4 );
+//---------------------------------------------------------------------------
 
-    // vertex shader - subgraph
-    std::string vertexSource = Stringify()
-        << "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-        << "precision mediump float;\n"
-#endif
-        << "uniform mat4 oe_overlay_TexGenMatrix; \n"
-        << "uniform mat4 osg_ViewMatrixInverse; \n"
-        << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n"
-
-        << "void oe_overlay_vertex(void) \n"
-        << "{ \n"
-        << "    osg_TexCoord["<< *_textureUnit << "] = oe_overlay_TexGenMatrix * osg_ViewMatrixInverse * gl_ModelViewMatrix * gl_Vertex; \n"
-        << "} \n";
-
-    vp->setFunction( "oe_overlay_vertex", vertexSource, ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
-
-    // fragment shader - subgraph
-    std::string fragmentSource = Stringify()
-        << "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-        << "precision mediump float;\n"
-#endif
-        << "uniform sampler2D oe_overlay_ProjTex; \n"
-        << "varying vec4 osg_TexCoord[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n"
-        << "void oe_overlay_fragment( inout vec4 color ) \n"
-        << "{ \n"
-        << "    vec2 texCoord = osg_TexCoord["<< *_textureUnit << "].xy / osg_TexCoord["<< *_textureUnit << "].q; \n"
-        << "    vec4 texel = texture2D(oe_overlay_ProjTex, texCoord); \n"  
-        << "    color = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a); \n"
-        << "} \n";
-
-    vp->setFunction( "oe_overlay_fragment", fragmentSource, ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING );
+OverlayDecorator::OverlayDecorator() :
+_useShaders          ( false ),
+_dumpRequested       ( false ),
+_rttTraversalMask    ( ~0 ),
+_maxHorizonDistance  ( DBL_MAX ),
+_totalOverlayChildren( 0 )
+{
+    //nop.
 }
 
+
 void
-OverlayDecorator::setOverlayGraph( osg::Node* node )
+OverlayDecorator::addTechnique(OverlayTechnique* technique)
 {
-    if ( _overlayGraph.get() != node )
+    if ( _engine.valid() )
     {
-        if ( _overlayGraph.valid() && node == 0L )
-        {
-            // un-register for traversals.
-            if ( _updatePending )
-            {
-                _updatePending = false;
-                ADJUST_EVENT_TRAV_COUNT( this, -1 );
-            }
+        OE_WARN << LC <<
+            "Illegal: you cannot install any more techniques once the Decorator "
+            "has been installed by the terrain engine." << std::endl;
 
-            ADJUST_EVENT_TRAV_COUNT( this, -1 );
-        }
-        else if ( !_overlayGraph.valid() && node != 0L )
+    }
+
+    else if ( technique )
+    {
+        if ( technique->supported() )
         {
-            // request that OSG give this node an event traversal.
-            ADJUST_EVENT_TRAV_COUNT( this, 1 );
+            _overlayGroups.push_back( new NotifierGroup<OverlayDecorator>(this) );
+            _techniques.push_back( technique );
         }
-
-        _overlayGraph = node;
-
-        initializeForOverlayGraph();
-
-        // go through and install the NEW overlay graph on any existing cameras.
+        else
         {
-            Threading::ScopedWriteLock exclude( _perViewDataMutex );
-            for( PerViewDataMap::iterator i = _perViewData.begin(); i != _perViewData.end(); ++i )
-            {
-                PerViewData& pvd = i->second;
-                if ( pvd._rttCamera->getNumChildren() > 0 )
-                    pvd._rttCamera->replaceChild( 0, _overlayGraph.get() );
-                else
-                    pvd._rttCamera->addChild( _overlayGraph.get() );
-            }
+            // stick unsupported techniques in a temporary holding cell
+            // for reference management -- no harm
+            _unsupportedTechniques.push_back( technique );
         }
-
-        //reinit();
     }
 }
 
-void
-OverlayDecorator::setOverlayGraphTraversalMask( unsigned mask )
-{
-    _rttTraversalMask = mask;
-}
 
 void
-OverlayDecorator::setTextureSize( int texSize )
+OverlayDecorator::onGroupChanged(osg::Group* group)
 {
-    if ( texSize != _textureSize.value() )
-    {
-        _textureSize = texSize;
-        //reinit();
-    }
-}
+    // the group changed so we need to give the corresponding
+    // technique a chance to re-establish itself based on the 
+    // contents of that group.
 
-void
-OverlayDecorator::setTextureUnit( int texUnit )
-{
-    if ( !_explicitTextureUnit.isSet() || texUnit != _explicitTextureUnit.value() )
-    {
-        _explicitTextureUnit = texUnit;
-        //reinit();
-    }
-}
+    // update the total child count
+    _totalOverlayChildren = 0;
 
-void
-OverlayDecorator::setMipMapping( bool value )
-{
-    if ( value != _mipmapping )
+    for( unsigned i=0; i<_techniques.size(); ++i )
     {
-        _mipmapping = value;
-        //reinit();
+        //TODO: change to technique->getActive() or something
+        _totalOverlayChildren += _overlayGroups[i]->getNumChildren();
 
-        if ( _mipmapping )
-            OE_INFO << LC << "Overlay mipmapping " << (value?"enabled":"disabled") << std::endl;
+        if ( _overlayGroups[i] == group )
+        {
+            _techniques[i]->reestablish( _engine.get() );
+        }
     }
 }
 
+
 void
-OverlayDecorator::setOverlayBlending( bool value )
+OverlayDecorator::initializePerViewData( PerViewData& pvd, osg::Camera* cam )
 {
-    if ( value != _rttBlending )
+    pvd._camera = cam;
+    pvd._sharedTerrainStateSet = new osg::StateSet();
+
+    pvd._techParams.resize( _overlayGroups.size() );
+
+    for(unsigned i=0; i<_overlayGroups.size(); ++i )
     {
-        _rttBlending = value;
-        //reinit();
-        
-        if ( _rttBlending )
-            OE_INFO << LC << "Overlay blending " << (value?"enabled":"disabled")<< std::endl;
+        TechRTTParams& params = pvd._techParams[i];
+        params._group = _overlayGroups[i].get();
+        params._terrainStateSet = pvd._sharedTerrainStateSet.get(); // share it.
+        params._horizonDistance = &pvd._sharedHorizonDistance;      // share it.
+        params._terrainParent = this;
+        params._mainCamera = cam;
     }
 }
 
-bool
-OverlayDecorator::getAttachStencil() const
-{
-    return _attachStencil;
-}
 
 void
-OverlayDecorator::setAttachStencil( bool value )
+OverlayDecorator::setOverlayGraphTraversalMask( unsigned mask )
 {
-    _attachStencil = value;
+    _rttTraversalMask = mask;
 }
 
+
 void
 OverlayDecorator::onInstall( TerrainEngineNode* engine )
 {
@@ -457,68 +313,48 @@ OverlayDecorator::onInstall( TerrainEngineNode* engine )
         _maxProjectedMapExtent = osg::maximum( extent.width(), extent.height() );
     }
 
-    // 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.
+    //todo: need this? ... probably not anymore
     _useShaders = 
-        Registry::capabilities().supportsGLSL() && 
-        engine->getTextureCompositor()->usesShaderComposition();
+        Registry::capabilities().supportsGLSL() && (
+            !engine->getTextureCompositor() ||
+            engine->getTextureCompositor()->usesShaderComposition() );
 
-    if ( !_textureSize.isSet() )
+    for(Techniques::iterator t = _techniques.begin(); t != _techniques.end(); ++t )
     {
-        unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
-        _textureSize.init( osg::minimum( 4096u, maxSize ) );
-
-        OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
+        t->get()->onInstall( engine );
     }
-
-    // rebuild dynamic elements.
-    initializeForOverlayGraph();
 }
 
+
 void
 OverlayDecorator::onUninstall( TerrainEngineNode* engine )
 {
-    if ( !_explicitTextureUnit.isSet() && _textureUnit.isSet() )
+    for(Techniques::iterator t = _techniques.begin(); t != _techniques.end(); ++t )
     {
-        _engine->getTextureCompositor()->releaseTextureImageUnit( *_textureUnit );
-        _textureUnit.unset();
+        t->get()->onUninstall( engine );
     }
 
     _engine = 0L;
 }
 
-void
-OverlayDecorator::updateRTTCamera( OverlayDecorator::PerViewData& pvd )
-{
-    static osg::Matrix normalizeMatrix = 
-        osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5,0.5,0.5);
-
-    pvd._rttCamera->setViewMatrix( pvd._rttViewMatrix );
-    pvd._rttCamera->setProjectionMatrix( pvd._rttProjMatrix );
-
-    osg::Matrix MVPT = pvd._rttViewMatrix * pvd._rttProjMatrix * normalizeMatrix;
-
-    if ( pvd._texGenUniform.valid() )
-    {
-        pvd._texGenUniform->set( MVPT );
-    }
-    else
-    {
-        pvd._texGen->setPlanesFromMatrix( MVPT );
-    }
-}
 
 void
-OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData& pvd )
+OverlayDecorator::cullTerrainAndCalculateRTTParams(osgUtil::CullVisitor* cv,
+                                                   PerViewData&          pvd)
 {
     static int s_frame = 1;
+    static osg::Vec3d zero(0.0, 0.0, 0.0);
 
-    osg::Vec3 eye = cv->getEyePoint();
+    osg::Matrixd invViewMatrix = cv->getCurrentCamera()->getInverseViewMatrix();
+    osg::Vec3d eye = zero * invViewMatrix;
+    //osg::Vec3 eye = cv->getEyePoint();
 
     double eyeLen;
     osg::Vec3d worldUp;
 
+    // Radius at eyepoint (geocentric)
+    double R;
+
     // height above sea level
     double hasl;
 
@@ -526,17 +362,18 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
     double haslWeight;
 
     // approximate distance to the visible horizon
-    double horizonDistance; 
-
-    // distance to the horizon, projected into the RTT camera's tangent plane.
-    double horizonDistanceInRTTPlane;
+    double horizonDistance;
 
     OE_TEST << LC << "------- OD CULL ------------------------" << std::endl;
 
     if ( _isGeocentric )
     {
+        eyeLen = eye.length();
+
         double lat, lon;
         _ellipsoid->convertXYZToLatLongHeight( eye.x(), eye.y(), eye.z(), lat, lon, hasl );
+
+        R = eyeLen - hasl;
         
         //Actually sample the terrain to get the height and adjust the eye position so it's a tighter fit to the real data.
         double height;
@@ -544,24 +381,15 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
         {
             hasl -= height;
         }
-        hasl = osg::maximum( hasl, 100.0 );                
-
+        hasl = osg::maximum( hasl, 100.0 );
 
         worldUp = _ellipsoid->computeLocalUpVector(eye.x(), eye.y(), eye.z());
 
-        eyeLen = eye.length();
 
         // radius of the earth under the eyepoint
+        // gw: wrong. use R instead.
         double radius = eyeLen - hasl; 
-        horizonDistance = sqrt( 2.0 * radius * hasl ); 
-    
-        // calculate the distance to the horizon, projected into the RTT camera plane.
-        // This is the maximum limit of eMax since there is no point in drawing overlay
-        // data beyond the visible horizon.
-        double pitchAngleOfHorizon_rad = acos( horizonDistance/eyeLen );
-        horizonDistanceInRTTPlane = horizonDistance * sin( pitchAngleOfHorizon_rad );
-
-        OE_TEST << LC << "RTT distance to horizon: " << horizonDistanceInRTTPlane << std::endl;
+        horizonDistance = sqrt( 2.0*radius*hasl + hasl*hasl );
     }
     else // projected map
     {
@@ -570,12 +398,13 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
         worldUp.set( 0.0, 0.0, 1.0 );
         eyeLen = hasl * 2.0;
 
-        // there is no maximum horizon distance in a projected map
-        horizonDistance = DBL_MAX;
-        horizonDistanceInRTTPlane = DBL_MAX;
-
-        pvd._rttViewMatrix = osg::Matrixd::lookAt( eye, eye-worldUp*hasl, osg::Vec3(0,1,0) );
+        // there "horizon distance" in a projected map is infinity,
+        // so just simulate one.
+        horizonDistance = sqrt(2.0*6356752.3142*hasl + hasl*hasl);
     }
+    
+    // update the shared horizon distance.
+    pvd._sharedHorizonDistance = horizonDistance;
 
     // create a "weighting" that weights HASL against the camera's pitch.
     osg::Vec3d lookVector = cv->getLookVectorLocal();
@@ -588,13 +417,6 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
     osg::Vec3 camLookVec = to-from;
     camLookVec.normalize();
 
-    // unit look-vector of the RTT camera:
-    osg::Vec3d rttLookVec = -worldUp;
-
-    // the minimum and maximum extents of the overlay ortho projector:
-    double eMin = 0.1;
-    double eMax = DBL_MAX;
-
     // Save and reset the current near/far planes before traversing the subgraph.
     // We do this because we want a projection matrix that includes ONLY the clip
     // planes from the subgraph, and not anything traversed up to this point.
@@ -606,7 +428,7 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
 
     // cull the subgraph (i.e. the terrain) here. This doubles as the subgraph's official 
     // cull traversal and a gathering of its clip planes.
-    cv->pushStateSet( pvd._subgraphStateSet.get() );
+    cv->pushStateSet( pvd._sharedTerrainStateSet.get() );
     osg::Group::traverse( *cv );
     cv->popStateSet();
 
@@ -627,180 +449,164 @@ OverlayDecorator::cull( osgUtil::CullVisitor* cv, OverlayDecorator::PerViewData&
     cv->setCalculatedNearPlane( osg::minimum(zSavedNear, zNear) );
     cv->setCalculatedFarPlane( osg::maximum(zSavedFar, zFar) );
 
-    if ( _isGeocentric )
-    {        
-        // in geocentric mode, clamp the far clip plane to the horizon.
-        double maxDistance = (1.0 - haslWeight)  * horizonDistance  + haslWeight * hasl;
-        maxDistance = osg::clampBelow( maxDistance, _maxHorizonDistance );
-        maxDistance *= 1.5;
-        if (zFar - zNear >= maxDistance)
-            zFar = zNear + maxDistance;
+    // clamp the far plane (for RTT purposes) to the horizon distance.
+    double maxFar = std::min( horizonDistance, _maxHorizonDistance );
+    cv->clampProjectionMatrix( projMatrix, zNear, maxFar );
 
-        cv->clampProjectionMatrix( projMatrix, zNear, zFar );
-
-        OE_TEST << LC << "Horizon clamp: zNear = " << zNear << ", zFar = " << zFar << std::endl;
-    }
-       
-    // contruct the polyhedron representing the viewing frustum.
-    osgShadow::ConvexPolyhedron frustumPH;
-    //MyConvexPolyhedron frustumPH;
-    frustumPH.setToUnitFrustum( true, true );
+    // prepare to calculate the ideal far plane for RTT extent resolution.
     osg::Matrixd MVP = *cv->getModelViewMatrix() * projMatrix;
     osg::Matrixd inverseMVP;
     inverseMVP.invert(MVP);
-    frustumPH.transform( inverseMVP, MVP );
 
-    // take the bounds of the overlay graph, constrained to the view frustum:
-    osg::BoundingSphere visibleOverlayBS;
-    osg::Polytope frustumPT;
-    frustumPH.getPolytope( frustumPT );
+    double maxDist2 = 0.0;
 
-    // get a bounds of the overlay graph as a whole, and convert that to a
-    // bounding box. We can probably do better with a ComputeBoundsVisitor but it
-    // will be slower.
-    visibleOverlayBS = _overlayGraph->getBound();
-    osg::BoundingBox visibleOverlayBBox;
-    visibleOverlayBBox.expandBy( visibleOverlayBS );
+    // constrain the far plane.
+    // intersect the top corners of the projection volume since those are the farthest.
+    if ( _isGeocentric )
+    {
+        intersectClipRayWithSphere( -1.0, 1.0, inverseMVP, R, maxDist2 );
+        intersectClipRayWithSphere(  1.0, 1.0, inverseMVP, R, maxDist2 );
+    }
+    else // projected
+    {
+        intersectClipRayWithPlane( -1.0, 1.0, inverseMVP, maxDist2 );
+        if ( maxDist2 == 0.0 )
+            intersectClipRayWithPlane( 1.0, 1.0, inverseMVP, maxDist2 );
+        if ( maxDist2 == 0.0 )
+            intersectClipRayWithPlane( 0.0, 1.0, inverseMVP, maxDist2 );
+    }
 
-    // intersect that bound with the camera frustum:
-    osg::Polytope visibleOverlayPT;
-    visibleOverlayPT.setToBoundingBox( visibleOverlayBBox );
-    osgShadow::ConvexPolyhedron visiblePH( frustumPH );
-    visiblePH.cut( visibleOverlayPT );
+    // clamp down the far plane:
+    if ( maxDist2 != 0.0 )
+    {
+        maxFar = std::min( zNear+sqrt(maxDist2), maxFar );
+    }
 
-#if 0
-    // This method does not work. Like with larged paged feature sets.
+    // reset the projection matrix if we changed the far:
+    if ( maxFar != zFar )
+    {
+        setFar( projMatrix, maxFar );
+        MVP = *cv->getModelViewMatrix() * projMatrix;
+        inverseMVP.invert(MVP);
+    }
 
-    ComputeBoundsWithinFrustum cbwp( frustumPH, _srs.get(), _isGeocentric, visibleOverlayBS );
-    _overlayGraph->accept( cbwp );
+    // Build a polyhedron for the new frustum so we can slice it.
+    // TODO: do we really even need to slice it anymore? consider
+    osgShadow::ConvexPolyhedron frustumPH;
+    frustumPH.setToUnitFrustum(true, true);
+    frustumPH.transform( inverseMVP, MVP );
 
-    // convert the visible geometry bounding sphere into a world-space polytope:
-    osg::Polytope visiblePT;
-    osgShadow::ConvexPolyhedron visiblePH( frustumPH );
+    // extract the verts associated with the frustum's PH:
+    std::vector<osg::Vec3d> verts;
+    frustumPH.getPoints( verts );
 
-    // this intersects the viewing frustum with the subgraph's bounding box, basically giving us
-    // a "minimal" polyhedron containing all potentially visible geometry. (It can't be truly 
-    // minimal without clipping at the geometry level, but that would probably be too expensive.)
+    // calculate the new RTT matrices. All techniques will share the 
+    // same set. We could probably put these in the "shared" category
+    // and use pointers..todo.
+    osg::Matrix rttViewMatrix, rttProjMatrix;
 
-    computeWorldBoundingPolytope( visibleOverlayBS, _srs.get(), _isGeocentric, visiblePT );
-    visiblePH.cut( visiblePT );
-#endif
 
-    // calculate the extents for our orthographic RTT camera (clamping it to the
-    // visible horizon)
-    std::vector<osg::Vec3d> verts;
-    visiblePH.getPoints( verts );
+    // for a camera that cares about geometry (like the draping technique) it's important
+    // to include the geometry in the ortho-camera's Z range. But for a camera that just
+    // cares about the terrain depth (like the clamping technique) we want to constrain 
+    // the Ortho Z as mush as possible in order to maintain depth precision. Perhaps
+    // later we can split this out and have each technique calculation its own View and
+    // Proj matrix.
+
+    // For now: our RTT camera z range will be based on this equation:
+    double zspan = std::max(50000.0, hasl+25000.0);
 
     if ( _isGeocentric )
     {
-        // for a geocentric map, try to place the RTT camera position at an optimal point
-        // that will minimize the span of the RTT texture. Take the centroid of the 
-        // visible polyhedron and clamp it's distance to the eyepoint by half the horizon
-        // distance.
-        osg::BoundingBox box = visiblePH.computeBoundingBox();
-        osg::Vec3d bc = box.center();
-        osg::Vec3d eye2bc = eye - bc;
-        if ( eye2bc.length() > horizonDistance )
-        {
-            eye2bc.normalize();
-            bc = eye + eye2bc * 0.5*horizonDistance;
-        }
-        
-        rttLookVec = -bc;
-        rttLookVec.normalize();
-
-        double new_eMax;
-        getMinMaxExtentInSilhouette( bc, rttLookVec, verts, eMin, new_eMax );
-        eMax = std::min( eMax, new_eMax );
-        pvd._rttViewMatrix = osg::Matrixd::lookAt( bc, osg::Vec3d(0,0,0), osg::Vec3d(0,0,1) );
-        pvd._rttProjMatrix = osg::Matrixd::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, bc.length() );
-
-        OE_TEST << LC 
-            << "1/2 RTT ortho span: " << eMax << ", near=" << -eyeLen << ", far=" << bc.length() << std::endl;
-
-        OE_TEST << LC
-            << "eMax = " << eMax
-            << ", bc = " << bc.x() << ", " << bc.y() << ", " << bc.z()
-            << ", eye = " << eye.x() << ", " << eye.y() << ", " << eye.z()
-            << ", eyeLen = " << eyeLen
-            << std::endl;
+        rttViewMatrix.makeLookAt( eye+worldUp*zspan, osg::Vec3d(0,0,0), osg::Vec3d(0,0,1) );
     }
     else
     {
-        // for a projected map, just point the RTT straight down at the camera position.
-        // TODO: this could be optimized, probably.
-        double new_eMax;
-        getMinMaxExtentInSilhouette( from, osg::Vec3d(0,0,-1), verts, eMin, new_eMax );   
-        eMax = std::min( eMax, new_eMax ); 
-        pvd._rttProjMatrix = osg::Matrix::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, eyeLen );
+        rttViewMatrix.makeLookAt( eye+worldUp*zspan, eye-worldUp*zspan, osg::Vec3d(0,1,0) );
     }
 
-    //OE_NOTICE << LC << "EMIN = " << eMin << ", EMAX = " << eMax << std::endl;
+    // calculate an orthographic RTT projection matrix based on the view-space
+    // bounds of the vertex list (i.e. the extents surrounding the RTT camera 
+    // that bounds all the polyherdron verts in its XY plane)
+    double xmin, ymin, xmax, ymax, maxDist;
+    getExtentInSilhouette(rttViewMatrix, eye, verts, xmin, ymin, xmax, ymax, maxDist);
+    rttProjMatrix.makeOrtho(xmin, xmax, ymin, ymax, 0.0, std::min(maxDist,eyeLen)+zspan);
+
 
-    if ( _dumpRequested )
+    // now copy the RTT matrixes over to the techniques.
+    for( unsigned t=0; t<pvd._techParams.size(); ++t )
     {
-        static const char* fn = "convexpolyhedron.osg";
+        TechRTTParams& params = pvd._techParams[t];
 
-        // camera frustum:
+        // skip empty techniques
+        if ( !_techniques[t]->hasData(params) )
+            continue;
+
+        params._rttViewMatrix.set( rttViewMatrix );
+        params._rttProjMatrix.set( rttProjMatrix );
+
+        // service a "dump" of the polyhedrons for dubugging purposes
+        // (see osgearth_overlayviewer)
+        if ( _dumpRequested )
         {
-            osgShadow::ConvexPolyhedron frustumPH;
-            frustumPH.setToUnitFrustum( true, true );
-            osg::Matrixd MVP = *cv->getModelViewMatrix() * projMatrix;
-            osg::Matrixd inverseMVP;
-            inverseMVP.invert(MVP);
-            frustumPH.transform( inverseMVP, MVP );
-            frustumPH.dumpGeometry(0,0,0,fn);
-        }
-        osg::Node* camNode = osgDB::readNodeFile(fn);
-        camNode->setName("camera");
+            static const char* fn = "convexpolyhedron.osg";
 
-        //// visible PH or overlay:
-        //visiblePHBeforeCut.dumpGeometry(0,0,0,fn,osg::Vec4(0,1,1,1),osg::Vec4(0,1,1,.25));
-        //osg::Node* overlay = osgDB::readNodeFile(fn);
-        //overlay->setName("overlay");
+            // camera frustum:
+            {
+                frustumPH.dumpGeometry(0,0,0,fn);
+            }
+            osg::Node* camNode = osgDB::readNodeFile(fn);
+            camNode->setName("camera");
 
-        // visible overlay Polyherdron AFTER frustum intersection:
-        visiblePH.dumpGeometry(0,0,0,fn,osg::Vec4(1,.5,1,1),osg::Vec4(1,.5,0,.25));
-        osg::Node* intersection = osgDB::readNodeFile(fn);
-        intersection->setName("intersection");
+            //// visible PH or overlay:
+            //visiblePHBeforeCut.dumpGeometry(0,0,0,fn,osg::Vec4(0,1,1,1),osg::Vec4(0,1,1,.25));
+            //osg::Node* overlay = osgDB::readNodeFile(fn);
+            //overlay->setName("overlay");
 
-        // RTT frustum:
-        {
-            osgShadow::ConvexPolyhedron rttPH;
-            rttPH.setToUnitFrustum( true, true );
-            osg::Matrixd MVP = pvd._rttViewMatrix * pvd._rttProjMatrix;
-            osg::Matrixd inverseMVP;
-            inverseMVP.invert(MVP);
-            rttPH.transform( inverseMVP, MVP );
-            rttPH.dumpGeometry(0,0,0,fn,osg::Vec4(1,1,0,1),osg::Vec4(1,1,0,0.25));
+#if 0
+            // visible overlay Polyherdron AFTER frustum intersection:
+            visiblePH.dumpGeometry(0,0,0,fn,osg::Vec4(1,.5,1,1),osg::Vec4(1,.5,0,.25));
+            osg::Node* intersection = osgDB::readNodeFile(fn);
+            intersection->setName("intersection");
+#endif
+
+            // RTT frustum:
+            {
+                osgShadow::ConvexPolyhedron rttPH;
+                rttPH.setToUnitFrustum( true, true );
+                osg::Matrixd MVP = params._rttViewMatrix * params._rttProjMatrix;
+                osg::Matrixd inverseMVP;
+                inverseMVP.invert(MVP);
+                rttPH.transform( inverseMVP, MVP );
+                rttPH.dumpGeometry(0,0,0,fn,osg::Vec4(1,1,0,1),osg::Vec4(1,1,0,0.25));
+            }
+            osg::Node* rttNode = osgDB::readNodeFile(fn);
+            rttNode->setName("rtt");
+
+            // EyePoint
+            osg::Geode* dsg = new osg::Geode();
+            dsg->addDrawable( new osg::ShapeDrawable(new osg::Box(osg::Vec3f(0,0,0), 10.0f)));
+            osg::AutoTransform* dsgmt = new osg::AutoTransform();
+            dsgmt->setPosition( osg::Vec3d(0,0,0) * osg::Matrix::inverse(*cv->getModelViewMatrix()) );
+            dsgmt->setAutoScaleToScreen(true);
+            dsgmt->addChild( dsg );
+
+            osg::Group* g = new osg::Group();
+            g->getOrCreateStateSet()->setAttribute(new osg::Program(), 0);
+            g->addChild(camNode);
+            //g->addChild(overlay);
+            //g->addChild(intersection);
+            g->addChild(rttNode);
+            g->addChild(dsgmt);
+
+            _dump = g;
+            _dumpRequested = false;
         }
-        osg::Node* rttNode = osgDB::readNodeFile(fn);
-        rttNode->setName("rtt");
-
-        // EyePoint
-        osg::Geode* dsg = new osg::Geode();
-        dsg->addDrawable( new osg::ShapeDrawable(new osg::Box(osg::Vec3f(0,0,0), 10.0f)));
-        osg::AutoTransform* dsgmt = new osg::AutoTransform();
-        dsgmt->setPosition( osg::Vec3d(0,0,0) * osg::Matrix::inverse(*cv->getModelViewMatrix()) );
-        dsgmt->setAutoScaleToScreen(true);
-        dsgmt->addChild( dsg );
-
-        osg::Group* g = new osg::Group();
-        g->getOrCreateStateSet()->setAttribute(new osg::Program(), 0);
-        g->addChild(camNode);
-        //g->addChild(overlay);
-        g->addChild(intersection);
-        g->addChild(rttNode);
-        g->addChild(dsgmt);
-
-        _dump = g;
-        _dumpRequested = false;
     }
 }
 
 OverlayDecorator::PerViewData&
 OverlayDecorator::getPerViewData(osg::Camera* key)
-//OverlayDecorator::getPerViewData(osg::NodeVisitor* key)
 {
     // first check for it:
     {
@@ -808,9 +614,10 @@ OverlayDecorator::getPerViewData(osg::Camera* key)
         PerViewDataMap::iterator i = _perViewData.find(key);
         if ( i != _perViewData.end() )
         {
-            if ( !i->second._rttCamera.valid() )
-                initializePerViewData( i->second );
-
+            if ( !i->second._sharedTerrainStateSet.valid() )
+            {
+                initializePerViewData( i->second, key );
+            }
             return i->second;
         }
     }
@@ -825,59 +632,86 @@ OverlayDecorator::getPerViewData(osg::Camera* key)
             return i->second;
 
         PerViewData& pvd = _perViewData[key];
-        initializePerViewData(pvd);
+        initializePerViewData(pvd, key);
 
         return pvd;
     }    
 }
 
+
 void
 OverlayDecorator::traverse( osg::NodeVisitor& nv )
 {
-    if ( _overlayGraph.valid() && _textureUnit.isSet() )
+    if ( true ) //if (_totalOverlayChildren > 0 )
     {
         // 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() == nv.CULL_VISITOR )
         {
-            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( &nv );
+            osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
             osg::Camera* camera = cv->getCurrentCamera();
+
             if ( camera != 0L && (_rttTraversalMask & nv.getTraversalMask()) != 0 )
             {
                 PerViewData& pvd = getPerViewData( camera );
 
-                if (checkNeedsUpdate(pvd))
+                //TODO:
+                // check whether we need to recalculate the RTT camera params.
+                // don't do it if the main camera hasn't moved;
+                // also, tell the ClampingTech not to re-snap the depth texture
+                // unless something has changed (e.g. camera params, terrain bounds..?
+                // what about paging..?)
+
+                // technique-specific setup prior to traversing:
+                for(unsigned i=0; i<_techniques.size(); ++i)
                 {
-                    updateRTTCamera(pvd);
+                    _techniques[i]->preCullTerrain( pvd._techParams[i], cv );
                 }
 
-                if ( pvd._texGen.valid() )
+                // shared terrain culling pass:
+                cullTerrainAndCalculateRTTParams( cv, pvd );
+
+                // prep and traverse the RTT camera(s):
+                for(unsigned i=0; i<_techniques.size(); ++i)
                 {
-                    // FFP path only
-                    cv->getCurrentRenderBin()->getStage()->addPositionedTextureAttribute(
-                        *_textureUnit, cv->getModelViewMatrix(), pvd._texGen.get() );
+                    TechRTTParams& params = pvd._techParams[i];
+                    _techniques[i]->cullOverlayGroup( params, cv );
                 }
 
-                cull( cv, pvd );
+#if 0
+                // new CullVisitor to traverse the subgroups:
+                MyCullVisitor* mcv = static_cast<MyCullVisitor*>(cv);
 
-                pvd._rttCamera->accept( nv );
+                // prep and traverse the RTT camera(s):
+                for(unsigned i=0; i<_techniques.size(); ++i)
+                {
+                    TechRTTParams& params = pvd._techParams[i];
+
+                    //mcv->_mvp = params._rttViewMatrix * params._rttProjMatrix;
+                    mcv->_cullingFrustum->setToUnitFrustum( true, true );
+                    mcv->_cullingFrustum->transformProvidingInverse( params._rttProjMatrix );
+                    //mcv->_cullingFrustum->transformProvidingInverse( params._rttViewMatrix * params._rttProjMatrix );
+
+                    _techniques[i]->cullOverlayGroup( params, cv );
+                    //_techniques[i]->cullOverlayGroup( params, cullVisitor.get() );
+                }
+                
+                static_cast<MyCullVisitor*>(cv)->_cullingFrustum.unset();
+#endif
             }
             else
             {
                 osg::Group::traverse(nv);
             }
-
-            // debug-- (draws the overlay at its native location as well)
-            //_overlayGraph->accept(nv);
         }
 
         else
         {
-            // Some other type of visitor (like update or intersection). Skip the RTT camera
-            // and traverse the overlay graph directly.
-            if ( _overlayGraph.valid() )
+            // Some other type of visitor (like update or intersection). Skip the technique
+            // and traverse the geometry directly.
+            for(unsigned i=0; i<_overlayGroups.size(); ++i)
             {
-                _overlayGraph->accept( nv );
+                _overlayGroups[i]->accept( nv );
             }
 
             osg::Group::traverse( nv );
@@ -890,17 +724,8 @@ OverlayDecorator::traverse( osg::NodeVisitor& nv )
 }
 
 
-bool
-OverlayDecorator::checkNeedsUpdate( OverlayDecorator::PerViewData& pvd )
-{
-    return
-        pvd._rttCamera->getViewMatrix()       != pvd._rttViewMatrix ||
-        pvd._rttCamera->getProjectionMatrix() != pvd._rttProjMatrix ||
-        (_overlayGraph.valid() && _overlayGraph->getNumChildrenRequiringUpdateTraversal() > 0);
-}
-
-
-double OverlayDecorator::getMaxHorizonDistance( ) const
+double
+OverlayDecorator::getMaxHorizonDistance() const
 {
     return _maxHorizonDistance;
 }
diff --git a/src/osgEarth/DrapeableNode b/src/osgEarth/OverlayNode
similarity index 55%
copy from src/osgEarth/DrapeableNode
copy to src/osgEarth/OverlayNode
index 00bdfcd..04102f0 100644
--- a/src/osgEarth/DrapeableNode
+++ b/src/osgEarth/OverlayNode
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 
-#ifndef OSGEARTH_DRAPEABLE_NODE_H
-#define OSGEARTH_DRAPEABLE_NODE_H 1
+#ifndef OSGEARTH_OVERLAY_NODE_H
+#define OSGEARTH_OVERLAY_NODE_H 1
 
 #include <osgEarth/Common>
 #include <osgEarth/MapNodeObserver>
@@ -29,28 +29,29 @@ namespace osgEarth
     class MapNode;
 
     /**
-     * Base class for a graph that can be "draped" on the MapNode terrain
-     * using the overlay decorator.
-     *
-     * Usage: Create this node and put it anywhere in the scene graph. The
-     * subgraph of this node will be draped on the MapNode's terrain.
+     * Abstract base class for a node that injects geometry into the MapNode's
+     * OverlayDecorator.
      */
-    class OSGEARTH_EXPORT DrapeableNode : public osg::Group, public MapNodeObserver
+    class OSGEARTH_EXPORT OverlayNode : public osg::Group, 
+                                        public MapNodeObserver
     {
     public:
+        // signature for the function that provides the technique group.
+        typedef osg::Group* (*TechniqueProvider)(MapNode*);      
+        
+    public:
         /**
-         * Constructs a new drapeable node.
+         * Constructs a new overlay node.
          */
-        DrapeableNode( MapNode* mapNode, bool draped =true );
-
-        /** dtor */
-        virtual ~DrapeableNode() { }
+        OverlayNode( MapNode* mapNode, bool active, TechniqueProvider f =0L );
 
+    public:
         /**
-         * Whether to drape the node content on the mapnode terrain.
+         * Whether overlay is active. If true, the subgraph renders as a
+         * terrain overlay; If false it renders normally.
          */
-        void setDraped( bool value );
-        bool getDraped() const { return _draped; }
+        void setActive( bool value );
+        bool getActive() const { return _active; }
 
     public: // MapNodeObserver
 
@@ -58,6 +59,9 @@ namespace osgEarth
 
         MapNode* getMapNode() { return _mapNode.get(); }
 
+        /** Sets the method that returns the technique group or overlay nodes */
+        void setTechniqueProvider(TechniqueProvider provider);
+
     public: // osg::Node
 
         virtual void traverse( osg::NodeVisitor& nv );
@@ -70,16 +74,21 @@ namespace osgEarth
         virtual bool removeChild( osg::Node* child );
         virtual bool replaceChild( osg::Node* origChild, osg::Node* newChild );
 
+    protected:
+        /** dtor */
+        virtual ~OverlayNode() { }
+
     private:
-        bool                       _draped;
-        bool                       _dirty;
-        bool                       _newDraped;
-        osg::ref_ptr<osg::Group>   _overlayProxyContainer;
-        osg::observer_ptr<MapNode> _mapNode;
+        bool                          _active;
+        bool                          _dirty;
+        bool                          _newActive;
+        osg::ref_ptr<osg::Group>      _overlayProxyContainer;
+        osg::observer_ptr<MapNode>    _mapNode;
+        TechniqueProvider             _getGroup;
 
         void applyChanges();
     };
 
 } // namespace osgEarth
 
-#endif // OSGEARTH_DRAPEABLE_NODE_H
+#endif // OSGEARTH_OVERLAY_NODE_H
diff --git a/src/osgEarth/DrapeableNode.cpp b/src/osgEarth/OverlayNode.cpp
similarity index 76%
copy from src/osgEarth/DrapeableNode.cpp
copy to src/osgEarth/OverlayNode.cpp
index 1d59d5f..56b8a90 100644
--- a/src/osgEarth/DrapeableNode.cpp
+++ b/src/osgEarth/OverlayNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,23 +17,23 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 
-#include <osgEarth/DrapeableNode>
+#include <osgEarth/OverlayNode>
 #include <osgEarth/CullingUtils>
 #include <osgEarth/OverlayDecorator>
 #include <osgEarth/MapNode>
 #include <osgEarth/NodeUtils>
 #include <osgUtil/IntersectionVisitor>
 
-#define LC "[DrapeableNode] "
+#define LC "[OverlayNode] "
 
 using namespace osgEarth;
 
 namespace
 {
     /**
-     * When draping is enabled, the actual draped graph goes under an OverlayProxy
+     * When draping is enabled, the actual active 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
+     * itself and applies it to the active geometry (which is installed under the
      * MapNode's OverlayDecorator).
      */
     struct OverlayProxy : public osg::Group
@@ -79,7 +79,7 @@ namespace
 
                         if ( i >= 0 && i < (int)ownerPath.size()-1 )
                         {
-                            osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
+                            osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
 
                             int pushes = 0;
                             for( int k = i+1; k < (int)ownerPath.size(); ++k )
@@ -135,14 +135,15 @@ namespace
 
 //------------------------------------------------------------------------
 
-DrapeableNode::DrapeableNode( MapNode* mapNode, bool draped ) :
-_newDraped( draped ),
-_draped   ( false ),
-_dirty    ( false )
+OverlayNode::OverlayNode( MapNode* mapNode, bool active, OverlayNode::TechniqueProvider provider ) :
+_newActive( active ),
+_active   ( false ),
+_dirty    ( false ),
+_getGroup ( provider )
 {
     // create a container group that will house the culler. This culler
-    // allows a draped node, which sits under the MapNode's OverlayDecorator,
-    // to "track" the traversal state of the DrapeableNode itself.
+    // allows a active node, which sits under the MapNode's OverlayDecorator,
+    // to "track" the traversal state of the OverlayNode itself.
     _overlayProxyContainer = new OverlayProxy( this );
 
     setMapNode( mapNode );
@@ -151,25 +152,22 @@ _dirty    ( false )
     {
         // If draping is requested, set up to apply it on the first update traversal.
         // Can't apply it until then since we need safe access to the MapNode.
-        setDraped( draped );
-    }
-    else
-    {
-        OE_DEBUG << LC << "Creates a drapeable without a MapNode; draping will be disabled" << std::endl;
+        setActive( active );
     }
 }
 
 void
-DrapeableNode::setMapNode( MapNode* mapNode )
+OverlayNode::setMapNode( MapNode* mapNode )
 {
     MapNode* oldMapNode = getMapNode();
 
     if ( oldMapNode != mapNode )
     {
-        if ( oldMapNode && _draped && _overlayProxyContainer->getNumParents() > 0 )
+        if ( oldMapNode && _getGroup && _active && _overlayProxyContainer->getNumParents() > 0 )
         {
-            oldMapNode->getOverlayGroup()->removeChild( _overlayProxyContainer.get() );
-            oldMapNode->updateOverlayGraph();
+            osg::Group* group = _getGroup( oldMapNode );
+            if ( group )
+                group->removeChild( _overlayProxyContainer.get() );
         }
 
         _mapNode = mapNode;
@@ -179,21 +177,36 @@ DrapeableNode::setMapNode( MapNode* mapNode )
 }
 
 void
-DrapeableNode::applyChanges()
+OverlayNode::setTechniqueProvider( OverlayNode::TechniqueProvider p )
+{
+    osg::ref_ptr<MapNode> save = getMapNode();
+    if ( save.valid() )
+        setMapNode( 0L );
+
+    _getGroup = p;
+
+    if ( save.valid() )
+        setMapNode( save.get() );
+}
+
+void
+OverlayNode::applyChanges()
 {
-    _draped = _newDraped;
+    _active = _newActive;
 
-    if ( getMapNode() )
+    if ( getMapNode() && _getGroup )
     {
-        if ( _draped && _overlayProxyContainer->getNumParents() == 0 )
+        if ( _active && _overlayProxyContainer->getNumParents() == 0 )
         {
-            getMapNode()->getOverlayGroup()->addChild( _overlayProxyContainer.get() );
-            getMapNode()->updateOverlayGraph();
+            osg::Group* group = _getGroup( getMapNode() );
+            if ( group )
+                group->addChild( _overlayProxyContainer.get() );
         }
-        else if ( !_draped && _overlayProxyContainer->getNumParents() > 0 )
+        else if ( !_active && _overlayProxyContainer->getNumParents() > 0 )
         {
-            getMapNode()->getOverlayGroup()->removeChild( _overlayProxyContainer.get() );
-            getMapNode()->updateOverlayGraph();
+            osg::Group* group = _getGroup( getMapNode() );
+            if ( group )
+                group->removeChild( _overlayProxyContainer.get() );
         }
 
         dirtyBound();
@@ -201,11 +214,11 @@ DrapeableNode::applyChanges()
 }
 
 void
-DrapeableNode::setDraped( bool draped )
+OverlayNode::setActive( bool active )
 {    
-    if ( draped != _draped && getMapNode() )
+    if ( active != _active )
     {        
-        _newDraped = draped;
+        _newActive = active;
         if ( !_dirty )
         {
             _dirty = true;
@@ -215,7 +228,7 @@ DrapeableNode::setDraped( bool draped )
 }
 
 bool
-DrapeableNode::addChild( osg::Node* child )
+OverlayNode::addChild( osg::Node* child )
 {
     bool ok = osg::Group::addChild( child );
     if ( _overlayProxyContainer.valid() )
@@ -224,7 +237,7 @@ DrapeableNode::addChild( osg::Node* child )
 }
 
 bool
-DrapeableNode::insertChild( unsigned i, osg::Node* child )
+OverlayNode::insertChild( unsigned i, osg::Node* child )
 {
     bool ok = osg::Group::insertChild( i, child );
     if ( _overlayProxyContainer.valid() )
@@ -233,7 +246,7 @@ DrapeableNode::insertChild( unsigned i, osg::Node* child )
 }
 
 bool
-DrapeableNode::removeChild( osg::Node* child )
+OverlayNode::removeChild( osg::Node* child )
 {
     bool ok = osg::Group::removeChild( child );
     if ( _overlayProxyContainer.valid() )
@@ -242,7 +255,7 @@ DrapeableNode::removeChild( osg::Node* child )
 }
 
 bool
-DrapeableNode::replaceChild( osg::Node* oldChild, osg::Node* newChild )
+OverlayNode::replaceChild( osg::Node* oldChild, osg::Node* newChild )
 {
     bool ok = osg::Group::replaceChild( oldChild, newChild );
     if ( _overlayProxyContainer.valid() )
@@ -251,7 +264,7 @@ DrapeableNode::replaceChild( osg::Node* oldChild, osg::Node* newChild )
 }
 
 void
-DrapeableNode::traverse( osg::NodeVisitor& nv )
+OverlayNode::traverse( osg::NodeVisitor& nv )
 {
     if ( !_overlayProxyContainer.valid() )
     {
@@ -261,13 +274,13 @@ DrapeableNode::traverse( osg::NodeVisitor& nv )
     {
         if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
         {
-            if ( _draped )
+            if ( _active )
             {
                 // do nothing -- culling will happen via the OverlayProxy instead.
             }
             else
             {
-                // for a non-draped node, just traverse children as usual.
+                // for a non-active node, just traverse children as usual.
                 osg::Group::traverse( nv );
             }
         }
@@ -281,7 +294,7 @@ DrapeableNode::traverse( osg::NodeVisitor& nv )
                 ADJUST_UPDATE_TRAV_COUNT( this, -1 );
             }
             
-            // traverse children directly, regardles of draped status
+            // traverse children directly, regardles of active status
             osg::Group::traverse( nv );
         }
 
diff --git a/src/osgEarth/Pickers b/src/osgEarth/Pickers
index d7bdefe..6adbda6 100644
--- a/src/osgEarth/Pickers
+++ b/src/osgEarth/Pickers
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Pickers.cpp b/src/osgEarth/Pickers.cpp
index a24c4af..8385733 100644
--- a/src/osgEarth/Pickers.cpp
+++ b/src/osgEarth/Pickers.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Profile b/src/osgEarth/Profile
index 2386f07..a4bc7d6 100644
--- a/src/osgEarth/Profile
+++ b/src/osgEarth/Profile
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -172,10 +172,15 @@ namespace osgEarth
 
         /**
          * Gets the tile keys that comprise the tiles at the root (LOD 0) of this
-         * profile.
+         * profile. Same as calling getAllKeysAtLOD(0).
          */
         void getRootKeys(std::vector<TileKey>& out_keys ) const;
 
+        /**
+         * Gets all the tile keys at the specified LOD.
+         */
+        void getAllKeysAtLOD(unsigned lod, std::vector<TileKey>& out_keys) const;
+
         /** 
          * Calculates an extent given a tile location in this profile.
          */
diff --git a/src/osgEarth/Profile.cpp b/src/osgEarth/Profile.cpp
index f5ae251..a14c3f4 100644
--- a/src/osgEarth/Profile.cpp
+++ b/src/osgEarth/Profile.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -405,14 +405,22 @@ Profile::overrideSRS( const SpatialReference* srs ) const
 void
 Profile::getRootKeys( std::vector<TileKey>& out_keys ) const
 {
+    getAllKeysAtLOD(0, out_keys);
+}
+
+void
+Profile::getAllKeysAtLOD( unsigned lod, std::vector<TileKey>& out_keys ) const
+{
     out_keys.clear();
 
-    for (unsigned int c = 0; c < _numTilesWideAtLod0; ++c)
+    unsigned tx, ty;
+    getNumTiles( lod, tx, ty );
+
+    for(unsigned c=0; c<tx; ++c)
     {
-        for (unsigned int r = 0; r < _numTilesHighAtLod0; ++r)
+        for(unsigned r=0; r<ty; ++r)
         {
-            //TODO: upgrade to support multi-face profile:
-            out_keys.push_back( TileKey(0, c, r, this) ); // lod, x, y, profile
+            out_keys.push_back( TileKey(lod, c, r, this) );
         }
     }
 }
diff --git a/src/osgEarth/Progress b/src/osgEarth/Progress
index 767be74..dc2e98e 100644
--- a/src/osgEarth/Progress
+++ b/src/osgEarth/Progress
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Progress.cpp b/src/osgEarth/Progress.cpp
index 5dcf059..94525f3 100644
--- a/src/osgEarth/Progress.cpp
+++ b/src/osgEarth/Progress.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Random b/src/osgEarth/Random
index f5720f7..2dff36f 100644
--- a/src/osgEarth/Random
+++ b/src/osgEarth/Random
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Random.cpp b/src/osgEarth/Random.cpp
index 11eb497..9fe2282 100644
--- a/src/osgEarth/Random.cpp
+++ b/src/osgEarth/Random.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Registry b/src/osgEarth/Registry
index d691ec1..98f1c1c 100644
--- a/src/osgEarth/Registry
+++ b/src/osgEarth/Registry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -129,12 +129,15 @@ namespace osgEarth
          * shader factory if you want to alter any of osgEarth's baseline shaders
          * (advanced usage).
          */
-        ShaderFactory* getShaderFactory() const;
+        const ShaderFactory* getShaderFactory() const;
         void setShaderFactory( ShaderFactory* lib );
+        static const ShaderFactory* shaderFactory() { return instance()->getShaderFactory(); }
 
         /**
          * A default StateSetCache to use by any process that uses one.
          * A StateSetCache assist in stateset sharing across multiple nodes.
+         * Note: A registry-wide SSC is only supported in OSG 3.1.4+. See
+         * the Registry.cpp comments for details.
          */
         StateSetCache* getStateSetCache() const;
         void setStateSetCache( StateSetCache* cache );
@@ -218,9 +221,13 @@ namespace osgEarth
 
         osg::ref_ptr<TaskServiceManager> _taskServiceManager;
 
-        int _uidGen;
+        // unique ID generator:
+        int                      _uidGen;
+        mutable Threading::Mutex _uidGenMutex;
 
+        // system capabilities:
         osg::ref_ptr< Capabilities > _caps;
+        mutable Threading::Mutex     _capsMutex;
         void initCapabilities();
 
         osg::ref_ptr<osgDB::Options> _defaultOptions;
@@ -230,7 +237,8 @@ namespace osgEarth
         osg::ref_ptr<osgText::Font> _defaultFont;
 
         typedef std::vector<const Units*> UnitsVector;
-        UnitsVector _unitsVector;
+        UnitsVector                       _unitsVector;
+        mutable Threading::ReadWriteMutex _unitsVectorMutex;
 
         osg::ref_ptr<StateSetCache> _stateSetCache;
 
diff --git a/src/osgEarth/Registry.cpp b/src/osgEarth/Registry.cpp
index dbaf68f..4cc3293 100644
--- a/src/osgEarth/Registry.cpp
+++ b/src/osgEarth/Registry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
 #include <osgEarth/IOTypes>
 #include <osgEarth/ColorFilter>
 #include <osgEarth/StateSetCache>
+#include <osgEarth/HTTPClient>
 #include <osgEarthDrivers/cache_filesystem/FileSystemCache>
 #include <osg/Notify>
 #include <osg/Version>
@@ -56,18 +57,27 @@ _numGdalMutexGets   ( 0 ),
 _uidGen             ( 0 ),
 _caps               ( 0L ),
 _defaultFont        ( 0L ),
-_terrainEngineDriver( "osgterrain" )
+_terrainEngineDriver( "mp" )
 {
     // set up GDAL and OGR.
     OGRRegisterAll();
     GDALAllRegister();
 
+    // global initialization for CURL (not thread safe)
+    HTTPClient::globalInit();
+
+    // generates the basic shader code for the terrain engine and model layers.
     _shaderLib = new ShaderFactory();
+
+    // thread pool for general use
     _taskServiceManager = new TaskServiceManager();
+
+    // optimizes sharing of state attributes and state sets for
+    // performance boost
     _stateSetCache = new StateSetCache();
 
     // activate KMZ support
-    osgDB::Registry::instance()->addArchiveExtension  ( "kmz" );    
+    osgDB::Registry::instance()->addArchiveExtension  ( "kmz" );
     osgDB::Registry::instance()->addFileExtensionAlias( "kmz", "kml" );
 
     osgDB::Registry::instance()->addMimeTypeExtensionMapping( "application/vnd.google-earth.kml+xml", "kml" );
@@ -122,7 +132,6 @@ _terrainEngineDriver( "osgterrain" )
         OE_INFO << LC << "NO-CACHE MODE set from environment variable" << std::endl;
     }
 
-    // set the default terrain engine driver from the environment
     const char* teStr = ::getenv("OSGEARTH_TERRAIN_ENGINE");
     if ( teStr )
     {
@@ -130,7 +139,6 @@ _terrainEngineDriver( "osgterrain" )
     }
 
     // load a default font
-
     const char* envFont = ::getenv("OSGEARTH_DEFAULT_FONT");
     if ( envFont )
     {
@@ -142,6 +150,9 @@ _terrainEngineDriver( "osgterrain" )
         _defaultFont = osgText::readFontFile("arial.ttf");
 #endif
     }
+
+    // register the system stock Units.
+    Units::registerAll( this );
 }
 
 Registry::~Registry()
@@ -381,16 +392,15 @@ Registry::setCapabilities( Capabilities* caps )
     _caps = caps;
 }
 
-static OpenThreads::Mutex s_initCapsMutex;
 void
 Registry::initCapabilities()
 {
-    ScopedLock<Mutex> lock( s_initCapsMutex ); // double-check pattern (see getCapabilities)
+    ScopedLock<Mutex> lock( _capsMutex ); // double-check pattern (see getCapabilities)
     if ( !_caps.valid() )
         _caps = new Capabilities();
 }
 
-ShaderFactory*
+const ShaderFactory*
 Registry::getShaderFactory() const
 {
     return _shaderLib.get();
@@ -433,8 +443,7 @@ UID
 Registry::createUID()
 {
     //todo: use OpenThreads::Atomic for this
-    static Mutex s_uidGenMutex;
-    ScopedLock<Mutex> lock( s_uidGenMutex );
+    ScopedLock<Mutex> exclusive( _uidGenMutex );
     return (UID)( _uidGen++ );
 }
 
@@ -458,12 +467,15 @@ Registry::cloneOrCreateOptions( const osgDB::Options* input ) const
 void
 Registry::registerUnits( const Units* units )
 {
+    Threading::ScopedWriteLock exclusive( _unitsVectorMutex );
     _unitsVector.push_back(units);
 }
 
 const Units*
 Registry::getUnits(const std::string& name) const
 {
+    Threading::ScopedReadLock shared( _unitsVectorMutex );
+
     std::string lower = toLower(name);
     for( UnitsVector::const_iterator i = _unitsVector.begin(); i != _unitsVector.end(); ++i )
     {
diff --git a/src/osgEarth/Revisioning b/src/osgEarth/Revisioning
index ac20587..be6bc00 100644
--- a/src/osgEarth/Revisioning
+++ b/src/osgEarth/Revisioning
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Revisioning.cpp b/src/osgEarth/Revisioning.cpp
index baba623..36381dd 100644
--- a/src/osgEarth/Revisioning.cpp
+++ b/src/osgEarth/Revisioning.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ShaderFactory b/src/osgEarth/ShaderFactory
index 7e8a429..522df6f 100644
--- a/src/osgEarth/ShaderFactory
+++ b/src/osgEarth/ShaderFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -34,47 +34,22 @@ namespace osgEarth
     public:
         /** Creates a vertex shader main(). */
         virtual osg::Shader* createVertexShaderMain(
-            const ShaderComp::FunctionLocationMap& functions =ShaderComp::FunctionLocationMap(),
-            bool useLightingShaders =true ) const;
+            const ShaderComp::FunctionLocationMap& functions) const;
 
         /** Creates a fragment shader main(). */
-        virtual osg::Shader* createFragmentShaderMain( 
-            const ShaderComp::FunctionLocationMap& functions =ShaderComp::FunctionLocationMap(),
-            bool useLightingShaders =true ) const;
+        virtual osg::Shader* createFragmentShaderMain(
+            const ShaderComp::FunctionLocationMap& functions) 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 ); 
+         * Install lighting shaders in a VirtualProgram.
          */
-        virtual osg::Shader* createDefaultLightingFragmentShader() const;
+        virtual void installLightingShaders(VirtualProgram* vp) const;
 
         /**
          * Builds a shader that executes an image filter chain.
@@ -86,7 +61,7 @@ namespace osgEarth
          */
         virtual osg::Uniform* createUniformForGLMode(
             osg::StateAttribute::GLMode      mode,
-            osg::StateAttribute::GLModeValue value );
+            osg::StateAttribute::GLModeValue value ) const;
 
         /** dtor */
         virtual ~ShaderFactory() { }
diff --git a/src/osgEarth/ShaderFactory.cpp b/src/osgEarth/ShaderFactory.cpp
old mode 100644
new mode 100755
index c61f7f3..d2849ac
--- a/src/osgEarth/ShaderFactory.cpp
+++ b/src/osgEarth/ShaderFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -29,21 +29,14 @@
 
 #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
 
+#define INDENT "    "
+
 
 using namespace osgEarth;
 using namespace osgEarth::ShaderComp;
@@ -57,58 +50,106 @@ ShaderFactory::getSamplerName( unsigned unit ) const
 
 
 osg::Shader*
-ShaderFactory::createVertexShaderMain(const FunctionLocationMap& functions,
-                                      bool  useLightingShaders ) const
+ShaderFactory::createVertexShaderMain(const FunctionLocationMap& functions) const
 {
-    FunctionLocationMap::const_iterator i = functions.find( LOCATION_VERTEX_PRE_TEXTURING );
-    const OrderedFunctionMap* preTexture = i != functions.end() ? &i->second : 0L;
+    // collect the "model" stage functions:
+    FunctionLocationMap::const_iterator i = functions.find( LOCATION_VERTEX_MODEL );
+    const OrderedFunctionMap* modelStage = 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;
+    // collect the "view" stage functions:
+    FunctionLocationMap::const_iterator j = functions.find( LOCATION_VERTEX_VIEW );
+    const OrderedFunctionMap* viewStage = 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;
+    // collect the "clip" stage functions:
+    FunctionLocationMap::const_iterator k = functions.find( LOCATION_VERTEX_CLIP );
+    const OrderedFunctionMap* clipStage = k != functions.end() ? &k->second : 0L;
 
+    // header:
     std::stringstream buf;
-    buf << "#version " << GLSL_VERSION_STR << "\n"
-        << PRECISION_MEDIUMP_FLOAT "\n"
-        << "void osgearth_vert_setupColoring(); \n";
+    buf << 
+        "#version " GLSL_VERSION_STR "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n";
+
+    // prototypes for model stage methods:
+    if ( modelStage )
+    {
+        for( OrderedFunctionMap::const_iterator i = modelStage->begin(); i != modelStage->end(); ++i )
+            buf << "void " << i->second << "(inout vec4 VertexMODEL); \n";
+    }
+
+    // prototypes for view stage methods:
+    if ( viewStage )
+    {
+        for( OrderedFunctionMap::const_iterator i = viewStage->begin(); i != viewStage->end(); ++i )
+            buf << "void " << i->second << "(inout vec4 VertexVIEW); \n";
+    }
 
-    if ( useLightingShaders )
-        buf << "void osgearth_vert_setupLighting(); \n";
+    // prototypes for clip stage methods:
+    if ( clipStage )
+    {
+        for( OrderedFunctionMap::const_iterator i = clipStage->begin(); i != clipStage->end(); ++i )
+            buf << "void " << i->second << "(inout vec4 VertexCLIP); \n";
+    }
 
-    if ( preTexture )
-        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
-            buf << "void " << i->second << "(); \n";
+    // main:
+    buf <<
+        "varying vec4 osg_FrontColor; \n"
+        "void main(void) \n"
+        "{ \n"
+        INDENT "osg_FrontColor = gl_Color; \n"
+        INDENT "vec4 vertex = gl_Vertex; \n";
 
-    if ( preLighting )
-        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
-            buf << "void " << i->second << "(); \n";
+    // call Model stage methods.
+    if ( modelStage )
+    {
+        for( OrderedFunctionMap::const_iterator i = modelStage->begin(); i != modelStage->end(); ++i )
+        {
+            buf << INDENT << i->second << "(vertex); \n";
+        }
+    }
 
-    if ( postLighting )
-        for( OrderedFunctionMap::const_iterator i = postLighting->begin(); i != postLighting->end(); ++i )
-            buf << "void " << i->second << "(); \n";
+    // call View stage methods.
+    if ( viewStage )
+    {
+        buf << INDENT "vertex = gl_ModelViewMatrix * vertex; \n";
 
-    buf << "void main(void) \n"
-        << "{ \n"
-        << "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n";
+        for( OrderedFunctionMap::const_iterator i = viewStage->begin(); i != viewStage->end(); ++i )
+        {
+            buf << INDENT << i->second << "(vertex); \n";
+        }
+    }
 
-    if ( preTexture )
-        for( OrderedFunctionMap::const_iterator i = preTexture->begin(); i != preTexture->end(); ++i )
-            buf << "    " << i->second << "(); \n";
+    // call Clip stage methods.
+    if ( clipStage )
+    {
+        if ( viewStage )
+        {
+            buf << INDENT "vertex = gl_ProjectionMatrix * vertex; \n";
+        }
+        else
+        {
+            buf << INDENT "vertex = gl_ModelViewProjectionMatrix * vertex; \n";
+        }
 
-    buf << "    osgearth_vert_setupColoring(); \n";
-    
-    if ( preLighting )
-        for( OrderedFunctionMap::const_iterator i = preLighting->begin(); i != preLighting->end(); ++i )
-            buf << "    " << i->second << "(); \n";
+        for( OrderedFunctionMap::const_iterator i = clipStage->begin(); i != clipStage->end(); ++i )
+        {
+            buf << INDENT << i->second << "(vertex); \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";
+    // finally, emit the position vertex.
+    if ( clipStage )
+    {
+        buf << INDENT "gl_Position = vertex; \n";
+    }
+    else if ( viewStage )
+    {
+        buf << INDENT "gl_Position = gl_ProjectionMatrix * vertex; \n";
+    }
+    else
+    {
+        buf << INDENT "gl_Position = gl_ModelViewProjectionMatrix * vertex; \n";
+    }
 
     buf << "} \n";
 
@@ -121,179 +162,72 @@ ShaderFactory::createVertexShaderMain(const FunctionLocationMap& functions,
 
 
 osg::Shader*
-ShaderFactory::createFragmentShaderMain(const FunctionLocationMap& functions,
-                                        bool  useLightingShaders ) const
+ShaderFactory::createFragmentShaderMain(const FunctionLocationMap& functions) const
 {
-    FunctionLocationMap::const_iterator i = functions.find( LOCATION_FRAGMENT_PRE_TEXTURING );
-    const OrderedFunctionMap* preTexture = i != functions.end() ? &i->second : 0L;
+    FunctionLocationMap::const_iterator i = functions.find( LOCATION_FRAGMENT_COLORING );
+    const OrderedFunctionMap* coloring = 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;
+    FunctionLocationMap::const_iterator j = functions.find( LOCATION_FRAGMENT_LIGHTING );
+    const OrderedFunctionMap* lighting = j != functions.end() ? &j->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";
+        << GLSL_DEFAULT_PRECISION_FLOAT << "\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 )
+    if ( coloring )
+    {
+        for( OrderedFunctionMap::const_iterator i = coloring->begin(); i != coloring->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 )
+    if ( lighting )
     {
-        buf << "    osg_TexCoord["<< i <<"] = gl_MultiTexCoord"<< i << "; \n";
+        for( OrderedFunctionMap::const_iterator i = lighting->begin(); i != lighting->end(); ++i )
+            buf << "void " << i->second << "( inout vec4 color ); \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 << 
+        "varying vec4 osg_FrontColor; \n"
+        "void main(void) \n"
+        "{ \n"
+        INDENT "vec4 color = osg_FrontColor; \n";
 
-    buf << "#version " << GLSL_VERSION_STR << "\n"
-        << PRECISION_MEDIUMP_FLOAT << "\n";
-    
-    buf << "varying vec4 osg_FrontColor; \n";
-    
-    if ( numTexImageUnits > 0 )
+    if ( coloring )
     {
-        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");
-        }
+        for( OrderedFunctionMap::const_iterator i = coloring->begin(); i != coloring->end(); ++i )
+            buf << INDENT << i->second << "( color ); \n";
     }
 
-    buf << "void osgearth_frag_applyColoring( inout vec4 color ) \n"
-        << "{ \n"
-        << "    color = color * osg_FrontColor; \n";
-    
-    if ( numTexImageUnits > 0 )
+    if ( lighting )
     {
-        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";
-        }
+        for( OrderedFunctionMap::const_iterator i = lighting->begin(); i != lighting->end(); ++i )
+            buf << INDENT << i->second << "( color ); \n";
     }
 
-    buf << "} \n";
+    buf << 
+        INDENT "gl_FragColor = color; \n"
+        "} \n";  
 
     std::string str;
     str = buf.str();
-
     osg::Shader* shader = new osg::Shader( osg::Shader::FRAGMENT, str );
-    shader->setName( FRAGMENT_APPLY_COLORING );
+    shader->setName( "main(frag)" );
     return shader;
 }
 
-#ifdef GLENNS_PER_VERTEX_LIGHTING
 
-osg::Shader*
-ShaderFactory::createDefaultLightingVertexShader() const
+void
+ShaderFactory::installLightingShaders(VirtualProgram* vp) const
 {
-    std::string str = Stringify() << 
-
+    const char* vs =
         "#version " GLSL_VERSION_STR "\n"
-        PRECISION_MEDIUMP_FLOAT "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n"
 
         "uniform bool oe_mode_GL_LIGHTING; \n"
         "varying vec4 oe_lighting_adjustment; \n"
-        "varying vec4 oe_zero_vec; \n"
+        "varying vec4 oe_lighting_zero_vec; \n"
 
-        "void osgearth_vert_setupLighting() \n"
+        "void oe_lighting_vertex(inout vec4 VertexMODEL) \n"
         "{ \n"
         "    oe_lighting_adjustment = vec4(1.0); \n"
         "    if (oe_mode_GL_LIGHTING) \n"
@@ -301,250 +235,56 @@ ShaderFactory::createDefaultLightingVertexShader() const
         "        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"
+
+        // NOTE: See comment in the fragment shader below for an explanation of
+        //       this oe_zero_vec value.
+        "        oe_lighting_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() <<
-
+    const char* fs =
         "#version " GLSL_VERSION_STR "\n"
-        PRECISION_MEDIUMP_FLOAT "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n"
 
         "varying vec4 oe_lighting_adjustment; \n"
-        "varying vec4 oe_zero_vec; \n"
+        "varying vec4 oe_lighting_zero_vec; \n"
 
          "uniform bool oe_mode_GL_LIGHTING; \n"
-         "void osgearth_frag_applyLighting( inout vec4 color ) \n"
+         "void oe_lighting_fragment( inout vec4 color ) \n"
          "{ \n"
+         //NOTE: The follow was changed from the single line
+         //      "color *= oe_lighting_adjustment" to the current code to fix
+         //      an issue on iOS devices.  Adding a varying vec4 value set to
+         //      (0.0,0.0,0.0,0.0) to the color should not make a difference,
+         //      but it is part of the solution to the issue we were seeing.
+         //      Without it and the additional lines of code, the globe was
+         //      rendering textureless (just a white surface with lighting).
          "    if ( oe_mode_GL_LIGHTING ) \n"
          "    { \n"
          "        float alpha = color.a; \n"
-         "        color = color * oe_lighting_adjustment + oe_zero_vec; \n"
+         "        color = color * oe_lighting_adjustment + oe_lighting_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;
+    vp->setFunction( "oe_lighting_vertex",   vs, ShaderComp::LOCATION_VERTEX_MODEL, 0.0 );
+    vp->setFunction( "oe_lighting_fragment", fs, ShaderComp::LOCATION_FRAGMENT_LIGHTING, 0.0 );
 }
 
 
 osg::Shader*
-ShaderFactory::createDefaultLightingFragmentShader() const
+ShaderFactory::createColorFilterChainFragmentShader(const std::string&      function, 
+                                                    const ColorFilterChain& chain ) 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";
+    buf << 
+        "#version " GLSL_VERSION_STR "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n";
 
     // write out the shader function prototypes:
     for( ColorFilterChain::const_iterator i = chain.begin(); i != chain.end(); ++i )
@@ -561,7 +301,7 @@ ShaderFactory::createColorFilterChainFragmentShader( const std::string& function
     for( ColorFilterChain::const_iterator i = chain.begin(); i != chain.end(); ++i )
     {
         ColorFilter* filter = i->get();
-        buf << "    " << filter->getEntryPointFunctionName() << "(slot, color);\n";
+        buf << INDENT << filter->getEntryPointFunctionName() << "(slot, color);\n";
     }
         
     buf << "} \n";
@@ -574,13 +314,13 @@ ShaderFactory::createColorFilterChainFragmentShader( const std::string& function
 
 osg::Uniform*
 ShaderFactory::createUniformForGLMode(osg::StateAttribute::GLMode      mode,
-                                      osg::StateAttribute::GLModeValue value)
+                                      osg::StateAttribute::GLModeValue value) const
 {
     osg::Uniform* u = 0L;
 
     if ( mode == GL_LIGHTING )
     {
-        osg::Uniform* u = new osg::Uniform(osg::Uniform::BOOL, "oe_mode_GL_LIGHTING");
+        u = new osg::Uniform(osg::Uniform::BOOL, "oe_mode_GL_LIGHTING");
         u->set( (value & osg::StateAttribute::ON) != 0 );
     }
 
diff --git a/src/osgEarth/ShaderGenerator b/src/osgEarth/ShaderGenerator
index 51fcafd..88d8b54 100644
--- a/src/osgEarth/ShaderGenerator
+++ b/src/osgEarth/ShaderGenerator
@@ -32,6 +32,11 @@ namespace osgEarth
      * Utility class that will traverse a scene graph and generate a
      * set of VirtualProgram attributes to render it as best it can using
      * shaders.
+     *
+     * Usage:
+     *
+     *   ShaderGenerator gen;
+     *   graph->accept( gen );
      */
     class OSGEARTH_EXPORT ShaderGenerator : public osg::NodeVisitor
     {
@@ -57,6 +62,10 @@ namespace osgEarth
 
         void apply( osg::Geode& );
 
+        void apply( osg::PagedLOD& );
+
+        void apply( osg::ProxyNode& );
+
     protected:
 
         void apply( osg::Drawable* );
@@ -74,4 +83,4 @@ namespace osgEarth
 
 } // namespace osgEarth
 
-#endif // OSGEARTH_DRAPEABLE_NODE_H
+#endif // OSGEARTH_SHADER_GENERATOR_H
diff --git a/src/osgEarth/ShaderGenerator.cpp b/src/osgEarth/ShaderGenerator.cpp
index 17419c7..1a800ad 100644
--- a/src/osgEarth/ShaderGenerator.cpp
+++ b/src/osgEarth/ShaderGenerator.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -25,14 +25,22 @@
 
 #include <osg/Drawable>
 #include <osg/Geode>
+#include <osg/PagedLOD>
+#include <osg/ProxyNode>
 #include <osg/Texture1D>
 #include <osg/Texture2D>
 #include <osg/Texture3D>
 #include <osg/TexEnv>
+#include <osg/TexGen>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReadFile>
 #include <osgText/Text>
 
 #define LC "[ShaderGenerator] "
 
+#define SHADERGEN_PL_EXTENSION "osgearth_shadergen"
+
 using namespace osgEarth;
 
 //------------------------------------------------------------------------
@@ -67,6 +75,52 @@ using namespace osgEarth;
 
 //------------------------------------------------------------------------
 
+struct OSGEarthShaderGenPseudoLoader : public osgDB::ReaderWriter
+{
+    OSGEarthShaderGenPseudoLoader()
+    {
+        this->supportsExtension( SHADERGEN_PL_EXTENSION, "ShaderGen pseudoloader" );
+    }
+
+    const char* className()
+    {
+        return "OSGEarth ShaderGen pseudoloader";
+    }
+
+    bool acceptsExtension(const std::string& extension) const
+    {
+        return osgDB::equalCaseInsensitive( extension, SHADERGEN_PL_EXTENSION );
+    }
+
+    ReadResult readObject(const std::string& filename, const osgDB::Options* options) const
+    {
+        return readNode( filename, options );
+    }
+
+    ReadResult readNode(const std::string& filename, const osgDB::Options* options) const
+    {
+        if ( !acceptsExtension(osgDB::getFileExtension(filename)) )
+            return ReadResult::FILE_NOT_HANDLED;
+
+        std::string stripped = osgDB::getNameLessExtension(filename);
+
+        OE_INFO << LC << "Loading " << stripped << " and generating shaders." << std::endl;
+        
+        osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(stripped, options);
+        if ( node )
+        {
+            ShaderGenerator gen( Registry::stateSetCache() );
+            node->accept( gen );
+        }
+
+        return node.valid() ? ReadResult(node.release()) : ReadResult::ERROR_IN_READING_FILE;
+    }
+};
+
+REGISTER_OSGPLUGIN(SHADERGEN_PL_EXTENSION, OSGEarthShaderGenPseudoLoader)
+
+//------------------------------------------------------------------------
+
 namespace
 {
     /**
@@ -138,9 +192,10 @@ ShaderGenerator::ShaderGenerator() :
 osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN )
 {
     _state = new StateEx();
-    _stateSetCache = new StateSetCache();
+    //_stateSetCache = new StateSetCache();
+    _stateSetCache = 0L;
     _defaultVP = new VirtualProgram();
-    _defaultVP->installDefaultColoringAndLightingShaders();
+    Registry::instance()->getShaderFactory()->installLightingShaders( _defaultVP.get() );
 }
 
 
@@ -148,9 +203,9 @@ ShaderGenerator::ShaderGenerator( StateSetCache* cache ) :
 osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN )
 {
     _state = new StateEx();
-    _stateSetCache = cache ? cache : new StateSetCache();
+    _stateSetCache = cache; // ? cache : new StateSetCache();
     _defaultVP = new VirtualProgram();
-    _defaultVP->installDefaultColoringAndLightingShaders();
+    Registry::instance()->getShaderFactory()->installLightingShaders( _defaultVP.get() );
 }
 
 
@@ -165,6 +220,10 @@ ShaderGenerator::apply( osg::Node& node )
         osg::ref_ptr<osg::StateSet> replacement;
         if ( processGeometry(ss.get(), replacement) )
         {
+            // optimize state set sharing
+            if ( _stateSetCache.valid() )
+                _stateSetCache->share(replacement, replacement);
+
             _state->popStateSet();
             node.setStateSet( replacement.get() );
             _state->pushStateSet( replacement.get() );
@@ -192,7 +251,13 @@ ShaderGenerator::apply( osg::Geode& geode )
         if ( processGeometry(ss.get(), replacement) )
         {
             _state->popStateSet();
+            
+            // optimize state set sharing
+            if ( _stateSetCache.valid() )
+                _stateSetCache->share(replacement, replacement);
+
             geode.setStateSet( replacement.get() );
+
             _state->pushStateSet( replacement.get() );
         }
     }
@@ -237,11 +302,57 @@ ShaderGenerator::apply( osg::Drawable* drawable )
             }
 
             _state->popStateSet();
+
+            // optimize state set sharing
+            if ( _stateSetCache.valid() && replacement.valid() )
+            {
+                if ( _stateSetCache->share(replacement, replacement) )
+                    drawable->setStateSet( replacement.get() );
+            }
         }
     }
 }
 
 
+void
+ShaderGenerator::apply(osg::PagedLOD& node)
+{
+    for( unsigned i=0; i<node.getNumFileNames(); ++i )
+    {
+        const std::string& filename = node.getFileName( i );
+        if (!filename.empty() && 
+            osgDB::getLowerCaseFileExtension(filename).compare(SHADERGEN_PL_EXTENSION) != 0 )
+        {
+            node.setFileName( i, Stringify() << filename << "." << SHADERGEN_PL_EXTENSION );
+        }
+    }
+
+    apply( static_cast<osg::LOD&>(node) );
+}
+
+
+void
+ShaderGenerator::apply(osg::ProxyNode& node)
+{
+    if ( node.getLoadingExternalReferenceMode() != osg::ProxyNode::LOAD_IMMEDIATELY )
+    {
+        // rewrite the filenames to include the shadergen pseudo-loader extension so
+        // that dynamically loaded children will have the same shadergen applied.
+        for( unsigned i=0; i<node.getNumFileNames(); ++i )
+        {
+            const std::string& filename = node.getFileName( i );
+            if (!filename.empty() && 
+                osgDB::getLowerCaseFileExtension(filename).compare(SHADERGEN_PL_EXTENSION) != 0 )
+            {
+                node.setFileName( i, Stringify() << filename << "." << SHADERGEN_PL_EXTENSION );
+            }
+        }
+    }
+
+    apply( static_cast<osg::Group&>(node) );
+}
+
+
 bool
 ShaderGenerator::processText( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& replacement )
 {
@@ -267,7 +378,7 @@ ShaderGenerator::processText( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& re
     std::string vertSrc =
         "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION "\n"
         "varying " MEDIUMP "vec4 " TEX_COORD_TEXT ";\n"
-        "void " VERTEX_FUNCTION "()\n"
+        "void " VERTEX_FUNCTION "(inout vec4 vertex_view)\n"
         "{ \n"
         INDENT TEX_COORD_TEXT " = gl_MultiTexCoord0;\n"
         "} \n";
@@ -286,9 +397,8 @@ ShaderGenerator::processText( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& re
         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 );
+    vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_VIEW );
+    vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_COLORING );
     replacement->getOrCreateUniform( SAMPLER_TEXT, osg::Uniform::SAMPLER_2D )->set( 0 );
 
     return replacement.valid();
@@ -314,7 +424,8 @@ ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<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) );
+    osg::ref_ptr<VirtualProgram> vp = 
+        dynamic_cast<VirtualProgram*>( ss->getAttribute(VirtualProgram::SA_TYPE) );
 
     // Check whether the lighting state has changed and install a mode uniform.
     if ( ss->getMode(GL_LIGHTING) != osg::StateAttribute::INHERIT )
@@ -322,9 +433,8 @@ ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>
         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.
-        replacement->addUniform( sf->createUniformForGLMode(GL_LIGHTING, value) );
+        replacement->addUniform( Registry::shaderFactory()->createUniformForGLMode(GL_LIGHTING, value) );
     }
 
     // if the stateset changes any texture attributes, we need a new virtual program:
@@ -350,7 +460,7 @@ ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>
         fragHead << "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION;
 
         // function declarations:
-        vertBody << "void " VERTEX_FUNCTION "()\n{\n";
+        vertBody << "void " VERTEX_FUNCTION "(inout vec4 vertex_view)\n{\n";
 
         fragBody << "void " FRAGMENT_FUNCTION "(inout vec4 color)\n{\n";
 
@@ -376,23 +486,73 @@ ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>
                     }
                 }
 
-                vertHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";
-                vertBody << INDENT << TEX_COORD << t << " = gl_MultiTexCoord" << t << ";\n";
+                osg::TexGen::Mode texGenMode = osg::TexGen::OBJECT_LINEAR;
+                osg::TexGen* texGen = dynamic_cast<osg::TexGen*>(state->getTextureAttribute(t, osg::StateAttribute::TEXGEN));
+                if ( texGen )
+                {
+                    texGenMode = texGen->getMode();
+                }
 
+                vertHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";
                 fragHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";
 
+                // handle different TexGen modes.
+                switch(texGenMode)
+                {
+                case osg::TexGen::SPHERE_MAP:
+                    vertBody 
+                        //todo: consolidate.
+                        << INDENT "{\n" // scope it in case there are > 1
+                        << INDENT "vec3 v = normalize(vec3(vertex_view));\n"
+                        << INDENT "vec3 n = normalize(gl_NormalMatrix * gl_Normal);\n"
+                        << INDENT "vec3 r = reflect(v, n);\n"
+                        << INDENT "float m = 2.0 * sqrt(r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0));\n"
+                        << INDENT TEX_COORD << t << ".s = r.x/m + 0.5;\n"
+                        << INDENT TEX_COORD << t << ".t = r.y/m + 0.5;\n"
+                        << INDENT "}\n";
+                    break;
+
+                default:
+                    vertBody 
+                        << INDENT << TEX_COORD << t << " = gl_MultiTexCoord" << t << ";\n";
+                    break;
+                }
+
+
                 if ( dynamic_cast<osg::Texture1D*>(tex) )
                 {
                     fragHead << "uniform sampler1D " SAMPLER << t << ";\n";
                     fragBody << INDENT "texel = texture1D(" SAMPLER << t << ", " TEX_COORD << t << ".x);\n";
                     replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_1D )->set( t );
                 }
+#if 1
                 else if ( dynamic_cast<osg::Texture2D*>(tex) )
                 {
                     fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
                     fragBody << INDENT "texel = texture2D(" SAMPLER << t << ", " TEX_COORD << t << ".xy);\n";
                     replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
                 }
+#else // embosser
+                else if ( dynamic_cast<osg::Texture2D*>(tex) )
+                {
+                    fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
+                    fragBody 
+                        << INDENT "{\n"
+                        << INDENT "float bs = 1.0/256.0;\n"
+                        << INDENT "vec4 bm = vec4(0.0);\n"
+                        << INDENT "float u = " TEX_COORD << t << ".x;\n"
+                        << INDENT "float v = " TEX_COORD << t << ".y;\n"
+                        << INDENT "texel = texture2D(" SAMPLER << t << ", vec2(u, v)); \n"
+                        << INDENT "bm = texture2D(" SAMPLER << t << ", vec2(u-bs, v-bs)) + \n"
+                        << INDENT "     texture2D(" SAMPLER << t << ", vec2(u-bs, v-bs)) - \n"
+                        << INDENT "     texel                                            - \n"
+                        << INDENT "     texture2D(" SAMPLER << t << ", vec2(u+bs, v+bs));  \n"
+                        << INDENT "texel *= vec4( 2.0*(bm.rgb+vec3(0.5,0.5,0.5)),1.0 );\n"
+                        << INDENT "}\n";
+
+                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
+                }
+#endif
                 else if ( dynamic_cast<osg::Texture3D*>(tex) )
                 {
                     fragHead << "uniform sampler3D " SAMPLER << t << ";\n";
@@ -445,8 +605,8 @@ ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>
         fragSrc = fragHead.str();
 
         // inject the shaders:
-        vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
-        vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
+        vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_VIEW );
+        vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_COLORING );
     }
 
     return replacement.valid();
diff --git a/src/osgEarth/ShaderUtils b/src/osgEarth/ShaderUtils
index b59b64f..e3b14b1 100644
--- a/src/osgEarth/ShaderUtils
+++ b/src/osgEarth/ShaderUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ShaderUtils.cpp b/src/osgEarth/ShaderUtils.cpp
index 4402220..1320661 100644
--- a/src/osgEarth/ShaderUtils.cpp
+++ b/src/osgEarth/ShaderUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -19,7 +19,7 @@
 #include <osgEarth/ShaderUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
-#include <osgUtil/CullVisitor>
+#include <osgEarth/CullingUtils>
 #include <list>
 
 using namespace osgEarth;
@@ -231,6 +231,8 @@ osg_LightProducts::osg_LightProducts(int id)
 std::string 
 osg_LightProducts::glslDefinition()
 {
+    //Note: it's important that there be NO linefeeds in here, since that would
+    // break the shader merging code.
     return
         "struct osg_LightProducts {"
         " vec4 ambient;"
@@ -335,6 +337,8 @@ void osg_LightSourceParameters::applyState(osg::StateSet* stateset)
 std::string
 osg_LightSourceParameters::glslDefinition()
 {
+    //Note: it's important that there be NO linefeeds in here, since that would
+    // break the shader merging code.
     return
         "struct osg_LightSourceParameters {"
         " vec4 ambient;"
@@ -397,7 +401,7 @@ UpdateLightingUniformsHelper::~UpdateLightingUniformsHelper()
 void
 UpdateLightingUniformsHelper::cullTraverse( osg::Node* node, osg::NodeVisitor* nv )
 {
-    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( nv );
+    osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
     if ( cv )
     {
         StateSetStack stateSetStack;
diff --git a/src/osgEarth/SparseTexture2DArray b/src/osgEarth/SparseTexture2DArray
index aaddef7..66e27d7 100644
--- a/src/osgEarth/SparseTexture2DArray
+++ b/src/osgEarth/SparseTexture2DArray
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/SparseTexture2DArray.cpp b/src/osgEarth/SparseTexture2DArray.cpp
index 3137fe9..d6ff162 100644
--- a/src/osgEarth/SparseTexture2DArray.cpp
+++ b/src/osgEarth/SparseTexture2DArray.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/SpatialReference b/src/osgEarth/SpatialReference
index b016eeb..9d74192 100644
--- a/src/osgEarth/SpatialReference
+++ b/src/osgEarth/SpatialReference
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -51,6 +51,7 @@ namespace osgEarth
          * the geodetic datum for the ellipsoid.
          */
         static SpatialReference* create( const std::string& init, const std::string& vinit ="" );
+        static SpatialReference* get   ( const std::string& init, const std::string& vinit ="" ) { return create(init,vinit); }
 
         /**
          * Attempts to create a spatial reference def from a pre-existing CSN, returning
@@ -140,7 +141,7 @@ namespace osgEarth
             osg::Vec3d&       out_local,
             double*           out_geodeticZ =0L ) const;
 
-
+#if 0
 
     public:  // ECEF transformations.
 
@@ -177,7 +178,7 @@ namespace osgEarth
          * The points are transformed in place.
          */
         bool transformFromECEF( std::vector<osg::Vec3d>& points ) const;
-
+#endif
 
     public: // extent transformations.
         
@@ -214,6 +215,9 @@ namespace osgEarth
         /** True if this is a projected SRS (i.e. local coordinate system) */
         virtual bool isProjected() const;
 
+        /** True if this is an ECEF system (geocentric/meters) */
+        virtual bool isECEF() const;
+
         /** Tests whether this SRS represents a Mercator projection. */
         bool isMercator() const;
 
@@ -286,6 +290,9 @@ namespace osgEarth
             which Z is expressed as height above the geodetic ellipsoid). */
         const SpatialReference* getGeodeticSRS() const;
 
+        /** Gets the ECEF reference system associated with this SRS' ellipsoid. */
+        const SpatialReference* getECEF() const;
+
         /** Gets the vertical datum. If null, this SRS uses a default geodetic vertical datum */
         const VerticalDatum* getVerticalDatum() const;
 
@@ -306,7 +313,7 @@ namespace osgEarth
         /** Creates a UTM (universal transverse mercator) projection in the UTM zone
             containing the specified longitude. NOTE: this is slightly faster than using
             basic tmerc (transverse mercator) above. */
-        const SpatialReference* createUTMFromLonLat( const Angular& lon, const Angular& lat ) const;
+        const SpatialReference* createUTMFromLonLat( const Angle& lon, const Angle& lat ) const;
 
         /** Creates a copy of this SRS, but flags the new SRS so that it will operate in
             Plate Carre mode for the purposes of world coordinate conversion. The SRS is
@@ -356,6 +363,7 @@ namespace osgEarth
         bool _is_user_defined;
         bool _is_ltp;
         bool _is_plate_carre;
+        bool _is_ecef;
         unsigned _ellipsoidId;
         std::string _name;
         Key _key;
@@ -367,6 +375,7 @@ namespace osgEarth
         osg::ref_ptr<osg::EllipsoidModel> _ellipsoid;
         osg::ref_ptr<SpatialReference>    _geo_srs;
         osg::ref_ptr<SpatialReference>    _geodetic_srs;  // _geo_srs with a NULL vdatum.
+        osg::ref_ptr<SpatialReference>    _ecef_srs;
         osg::ref_ptr<VerticalDatum>       _vdatum;
 
         typedef std::map<std::string,void*> TransformHandleCache;
diff --git a/src/osgEarth/SpatialReference.cpp b/src/osgEarth/SpatialReference.cpp
index 05d4d24..57831c7 100644
--- a/src/osgEarth/SpatialReference.cpp
+++ b/src/osgEarth/SpatialReference.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -93,6 +93,30 @@ namespace
         }
         return true;
     }
+
+    void geodeticToECEF(std::vector<osg::Vec3d>& points, const osg::EllipsoidModel* em)
+    {
+        for( unsigned i=0; i<points.size(); ++i )
+        {
+            double x, y, z;
+            em->convertLatLongHeightToXYZ(
+                osg::DegreesToRadians( points[i].y() ), osg::DegreesToRadians( points[i].x() ), points[i].z(),
+                x, y, z );
+            points[i].set( x, y, z );
+        }
+    }
+
+    void ECEFtoGeodetic(std::vector<osg::Vec3d>& points, const osg::EllipsoidModel* em)
+    {
+        for( unsigned i=0; i<points.size(); ++i )
+        {
+            double lat, lon, alt;
+            em->convertXYZToLatLongHeight(
+                points[i].x(), points[i].y(), points[i].z(),
+                lat, lon, alt );
+            points[i].set( osg::RadiansToDegrees(lon), osg::RadiansToDegrees(lat), alt );
+        }
+    }
 }
 
 //------------------------------------------------------------------------
@@ -360,6 +384,7 @@ _handle         ( handle ),
 _owns_handle    ( true ),
 _init_type      ( init_type ),
 _is_geographic  ( false ),
+_is_ecef        ( false ),
 _is_mercator    ( false ),
 _is_north_polar ( false ), 
 _is_south_polar ( false ),
@@ -379,7 +404,8 @@ _initialized   ( false ),
 _handle        ( handle ),
 _owns_handle   ( ownsHandle ),
 _is_ltp        ( false ),
-_is_plate_carre( false )
+_is_plate_carre( false ),
+_is_ecef       ( false )
 {
     //nop
 }
@@ -425,7 +451,15 @@ SpatialReference::isProjected() const
 {
     if ( !_initialized )
         const_cast<SpatialReference*>(this)->init();
-    return !_is_geographic;
+    return !_is_geographic && !_is_ecef;
+}
+
+bool
+SpatialReference::isECEF() const 
+{
+    if ( !_initialized )
+        const_cast<SpatialReference*>(this)->init();
+    return _is_ecef;
 }
 
 const std::string&
@@ -560,6 +594,9 @@ SpatialReference::_isEquivalentTo( const SpatialReference* rhs, bool considerVDa
         return false;
     }
 
+    if (isECEF() && rhs->isECEF())
+        return true;
+
     if ( considerVDatum && (_vdatum.get() != rhs->_vdatum.get()) )
         return false;
 
@@ -577,9 +614,9 @@ SpatialReference::_isEquivalentTo( const SpatialReference* rhs, bool considerVDa
 
     if (this->isGeographic() && rhs->isGeographic())
     {
-        return (
-            this->getEllipsoid()->getRadiusEquator() == rhs->getEllipsoid()->getRadiusEquator() &&
-            this->getEllipsoid()->getRadiusPolar() == rhs->getEllipsoid()->getRadiusPolar() );
+        return
+            osg::equivalent( getEllipsoid()->getRadiusEquator(), rhs->getEllipsoid()->getRadiusEquator() ) &&
+            osg::equivalent( getEllipsoid()->getRadiusPolar(), rhs->getEllipsoid()->getRadiusPolar() );
     }
 
     // last resort, since it requires the lock
@@ -664,6 +701,44 @@ SpatialReference::getGeodeticSRS() const
 }
 
 const SpatialReference*
+SpatialReference::getECEF() const
+{
+    if ( !_initialized )
+        const_cast<SpatialReference*>(this)->init();
+
+    if ( _is_ecef )
+        return this;
+
+    if ( !_ecef_srs.valid() )
+    {
+        const SpatialReference* geo = getGeographicSRS(); // before the lock please.
+
+        GDAL_SCOPED_LOCK;
+
+        if ( !_ecef_srs.valid() ) // double-check pattern
+        {
+            void* new_handle = OSRNewSpatialReference( NULL );
+            int err = OSRCopyGeogCSFrom( new_handle, geo->_handle );
+            if ( err == OGRERR_NONE )
+            {
+                // make a new geographic srs, and copy over the vertical datum
+                SpatialReference* ncthis = const_cast<SpatialReference*>(this);
+                ncthis->_ecef_srs = new SpatialReference( new_handle );
+                ncthis->_ecef_srs->_vdatum = 0L; // no vertical datum in ECEF
+                ncthis->_ecef_srs->_is_ecef = true;
+            }
+            else
+            {
+                OSRDestroySpatialReference( new_handle );
+                OE_WARN << LC << "Failed to initialize a ECEF SRS for " << getName() << std::endl;
+            }
+        }
+    }
+
+    return _ecef_srs.get();
+}
+
+const SpatialReference*
 SpatialReference::createTangentPlaneSRS( const osg::Vec3d& pos ) const
 {
     SpatialReference* result = 0L;
@@ -861,25 +936,23 @@ SpatialReference::createLocalToWorld(const osg::Vec3d& xyz, osg::Matrixd& out_lo
             return false;
         out_local2world = osg::Matrix::translate(world);
     }
+    else if ( isECEF() )
+    {
+        out_local2world = ECEF::createLocalToWorld(xyz);
+    }
     else
     {
         // convert MSL to HAE if necessary:
         osg::Vec3d geodetic;
-        if ( !isGeodetic() )
-        {
-            if ( !transform( xyz, getGeodeticSRS(), geodetic ) )
-                return false;
-        }
-        else
-        {
-            geodetic = xyz;
-        }
+        if ( !transform(xyz, getGeodeticSRS(), geodetic) )
+            return false;
 
-        getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight(
-            osg::DegreesToRadians( geodetic.y() ),
-            osg::DegreesToRadians( geodetic.x() ),
-            geodetic.z(),
-            out_local2world );
+        // then to ECEF:
+        osg::Vec3d ecef;
+        if ( !transform(geodetic, getGeodeticSRS()->getECEF(), ecef) )
+            return false;
+
+        out_local2world = ECEF::createLocalToWorld(ecef);
     }
     return true;
 }
@@ -950,6 +1023,21 @@ SpatialReference::transform(std::vector<osg::Vec3d>& points,
         return success;
     }
 
+    else if ( isECEF() && !outputSRS->isECEF() )
+    {
+        const SpatialReference* outputGeoSRS = outputSRS->getGeodeticSRS();
+        ECEFtoGeodetic(points, outputGeoSRS->getEllipsoid());
+        return outputGeoSRS->transform(points, outputSRS);
+    }
+
+    else if ( !isECEF() && outputSRS->isECEF() )
+    {
+        const SpatialReference* outputGeoSRS = outputSRS->getGeodeticSRS();
+        success = transform(points, outputGeoSRS);
+        geodeticToECEF(points, outputGeoSRS->getEllipsoid());
+        return success;
+    }
+
     // if the points are starting as geographic, do the Z's first to avoid an unneccesary
     // transformation in the case of differing vdatums.
     bool z_done = false;
@@ -1131,7 +1219,8 @@ SpatialReference::transformToWorld(const osg::Vec3d& input,
 {
     if ( (isGeographic() && !isPlateCarre()) || isCube() ) //isGeographic() && !_is_plate_carre )
     {
-        return transformToECEF(input, output);
+        return transform(input, getECEF(), output);
+        //return transformToECEF(input, output);
     }
     else // isProjected || _is_plate_carre
     {
@@ -1155,7 +1244,8 @@ SpatialReference::transformFromWorld(const osg::Vec3d& world,
 {
     if ( (isGeographic() && !isPlateCarre()) || isCube() ) //isGeographic() && !_is_plate_carre )
     {
-        return transformFromECEF(world, output, out_haeZ);
+        //return transformFromECEF(world, output, out_haeZ);
+        return getECEF()->transform(world, this, output);
     }
     else // isProjected || _is_plate_carre
     {
@@ -1179,6 +1269,7 @@ SpatialReference::transformFromWorld(const osg::Vec3d& world,
 }
 
 
+#if 0
 bool 
 SpatialReference::transformToECEF(const osg::Vec3d& input,
                                   osg::Vec3d&       output ) const
@@ -1275,6 +1366,7 @@ SpatialReference::transformFromECEF(std::vector<osg::Vec3d>& points) const
 
     return ok;
 }
+#endif
 
 double
 SpatialReference::transformUnits(double                  input,
@@ -1455,9 +1547,12 @@ SpatialReference::_init()
 {
     // set defaults:
     _is_user_defined = false; 
-    _is_contiguous = true;   
+    _is_contiguous = true;
     _is_cube = false;
-    _is_geographic = OSRIsGeographic( _handle ) != 0;
+    if ( _is_ecef )
+        _is_geographic = false;
+    else
+        _is_geographic = OSRIsGeographic( _handle ) != 0;
 
     // extract the ellipsoid parameters:
     int err;
diff --git a/src/osgEarth/StateSetCache b/src/osgEarth/StateSetCache
index d55be5c..56280ca 100644
--- a/src/osgEarth/StateSetCache
+++ b/src/osgEarth/StateSetCache
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/StateSetCache.cpp b/src/osgEarth/StateSetCache.cpp
index 610bf9b..7a0c64b 100644
--- a/src/osgEarth/StateSetCache.cpp
+++ b/src/osgEarth/StateSetCache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -165,9 +165,14 @@ StateSetCache::optimize(osg::Node* node)
         ShareStateAttributes v1( this );
         node->accept( v1 );
 
+        
+#if OSG_MIN_VERSION_REQUIRED(3,1,4)
         // replace all equivalent static statesets with a single instance
+        // only supported in OSG 3.1.4+ because of the Uniform mutex 
+        // protection.
         ShareStateSets v2( this );
         node->accept( v2 );
+#endif
     }
 }
 
@@ -175,6 +180,7 @@ StateSetCache::optimize(osg::Node* node)
 bool
 StateSetCache::eligible(osg::StateSet* stateSet) const
 {
+#if OSG_MIN_VERSION_REQUIRED(3,1,4)
     if ( !stateSet )
         return false;
 
@@ -192,6 +198,9 @@ StateSetCache::eligible(osg::StateSet* stateSet) const
     }
 
     return true;
+#else
+    return false;
+#endif
 }
 
 
@@ -221,29 +230,42 @@ StateSetCache::share(osg::ref_ptr<osg::StateSet>& input,
                      osg::ref_ptr<osg::StateSet>& output,
                      bool                         checkEligible)
 {
+    bool shared     = false;
+    bool shareattrs = true;
+
     if ( !checkEligible || eligible(input.get()) )
     {
         Threading::ScopedMutexLock lock( _mutex );
+        shareattrs = false;
 
         std::pair<StateSetSet::iterator,bool> result = _stateSetCache.insert( input );
         if ( result.second )
         {
             // first use
             output = input.get();
-            return false;
+            shareattrs = true;
+            shared = false;
         }
         else
         {
             // found a share!
             output = result.first->get();
-            return true;
+            shared = true;
         }
     }
     else
     {
         output = input.get();
-        return false;
+        shared = false;
     }
+
+    if ( shareattrs )
+    {
+        ShareStateAttributes sa(this);
+        sa.applyStateSet( input.get() );
+    }
+
+    return shared;
 }
 
 
diff --git a/src/osgEarth/StringUtils b/src/osgEarth/StringUtils
index e61f5a8..4fb54b4 100644
--- a/src/osgEarth/StringUtils
+++ b/src/osgEarth/StringUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -150,6 +150,15 @@ namespace osgEarth
         return str;
     }
 
+    // snips a substring and parses it.
+    template<typename T> inline bool
+    as(const std::string& in, unsigned start, unsigned len, T default_value) 
+    {
+        std::string buf;
+        std::copy( in.begin()+start, in.begin()+start+len, std::back_inserter(buf) );
+        return as<T>(buf, default_value);
+    }
+
     // converts a primitive to a string
     // TODO: precision??
     template<typename T> inline std::string
diff --git a/src/osgEarth/StringUtils.cpp b/src/osgEarth/StringUtils.cpp
index cc7ae83..a7584e6 100644
--- a/src/osgEarth/StringUtils.cpp
+++ b/src/osgEarth/StringUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TaskService b/src/osgEarth/TaskService
index ad07296..76511fd 100644
--- a/src/osgEarth/TaskService
+++ b/src/osgEarth/TaskService
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TaskService.cpp b/src/osgEarth/TaskService.cpp
index 2816b7f..59399d8 100644
--- a/src/osgEarth/TaskService.cpp
+++ b/src/osgEarth/TaskService.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Terrain b/src/osgEarth/Terrain
index 1aa276c..abf5a68 100644
--- a/src/osgEarth/Terrain
+++ b/src/osgEarth/Terrain
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Terrain.cpp b/src/osgEarth/Terrain.cpp
index 1dd2e52..b020820 100644
--- a/src/osgEarth/Terrain.cpp
+++ b/src/osgEarth/Terrain.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -96,8 +96,11 @@ Terrain::getHeight(osg::Node*              patch,
 
     if ( isGeocentric() )
     {
-        getSRS()->transformToECEF(start, start);
-        getSRS()->transformToECEF(end, end);
+        const SpatialReference* ecef = getSRS()->getECEF();
+        getSRS()->transform(start, ecef, start);
+        getSRS()->transform(end,   ecef, end);
+        //getSRS()->transformToECEF(start, start);
+        //getSRS()->transformToECEF(end, end);
     }
 
     osgUtil::LineSegmentIntersector* lsi = new osgUtil::LineSegmentIntersector(start, end);
diff --git a/src/osgEarth/TerrainEngineNode b/src/osgEarth/TerrainEngineNode
index a474b3b..6ffb837 100644
--- a/src/osgEarth/TerrainEngineNode
+++ b/src/osgEarth/TerrainEngineNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -173,6 +173,9 @@ namespace osgEarth
     public:
         virtual void onInstall( TerrainEngineNode* engine ) { }
         virtual void onUninstall( TerrainEngineNode* engine ) { }
+
+    protected:
+        virtual ~TerrainDecorator() { }
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/TerrainEngineNode.cpp b/src/osgEarth/TerrainEngineNode.cpp
index b80cc6d..3bc03d8 100644
--- a/src/osgEarth/TerrainEngineNode.cpp
+++ b/src/osgEarth/TerrainEngineNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
  */
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarth/Capabilities>
+#include <osgEarth/CullingUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/TextureCompositor>
 #include <osgEarth/NodeUtils>
@@ -213,9 +214,11 @@ TerrainEngineNode::preInitialize( const Map* map, const TerrainOptions& options
 
     // enable backface culling
     osg::StateSet* set = getOrCreateStateSet();
-    set->setAttributeAndModes( new osg::CullFace( osg::CullFace::BACK ), osg::StateAttribute::ON );
+    //set->setAttributeAndModes( new osg::CullFace( osg::CullFace::BACK ), osg::StateAttribute::ON );
+    set->setMode( GL_CULL_FACE, 1 );
 
     // elevation uniform
+    // NOTE: wrong...this should be per-CullVisitor...consider putting in the Culling::CullUserData
     _cameraElevationUniform = new osg::Uniform( osg::Uniform::FLOAT, "osgearth_CameraElevation" );
     _cameraElevationUniform->set( 0.0f );
     set->addUniform( _cameraElevationUniform.get() );
@@ -412,7 +415,7 @@ TerrainEngineNode::traverse( osg::NodeVisitor& nv )
             if ( !_terrainInterface->_updateOperationQueue.valid() ) // double check pattern
             {
                 //TODO: think, will this work with >1 view?
-                osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
+                osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
                 if ( cv->getCurrentCamera() )
                 {
                     osgViewer::View* view = dynamic_cast<osgViewer::View*>(cv->getCurrentCamera()->getView());
@@ -434,7 +437,7 @@ TerrainEngineNode::traverse( osg::NodeVisitor& nv )
         {
             _updateLightingUniformsHelper.cullTraverse( this, &nv );
 
-            osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
+            osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
             if ( cv )
             {
                 osg::Vec3d eye = cv->getEyePoint();
diff --git a/src/osgEarth/TerrainLayer b/src/osgEarth/TerrainLayer
index 7ac5ce6..fb2d105 100644
--- a/src/osgEarth/TerrainLayer
+++ b/src/osgEarth/TerrainLayer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -88,13 +88,7 @@ namespace osgEarth
          */
         optional<double>& maxResolution() { return _maxResolution; }
         const optional<double>& maxResolution() const { return _maxResolution; }
-
-        /**
-         * The maximum level that data should be queried for this layer.
-         */
-        optional<unsigned int>& maxDataLevel() { return _maxDataLevel; }
-        const optional<unsigned int>& maxDataLevel() const { return _maxDataLevel; }
-
+        
         /**
          * Whether to use this layer with the map. Setting this to false means that 
          * the layer will remain in its map model but will not be used by the 
@@ -185,8 +179,7 @@ namespace osgEarth
         optional<bool>              _enabled;
         optional<bool>              _visible;
         optional<unsigned>          _reprojectedTileSize;
-        optional<double>            _edgeBufferRatio;
-        optional<unsigned>          _maxDataLevel;
+        optional<double>            _edgeBufferRatio;        
 
         optional<std::string>       _cacheId;
         optional<std::string>       _cacheFormat;
@@ -263,12 +256,7 @@ namespace osgEarth
          * Gets the tile size of the this MapLayer
          */
         unsigned int getTileSize() const;
-
-        /**
-         * Gets the maximum data level of this MapLayer
-         */
-        unsigned int getMaxDataLevel() const;
-
+        
         /**
          * Whether the layer represents dynamic data, i.e. tile data that can change.
          */
@@ -311,6 +299,11 @@ namespace osgEarth
                 _runtimeOptions->cachePolicy()->usage() == CachePolicy::USAGE_CACHE_ONLY;
         }
 
+    public: // Layer interface
+
+        virtual SequenceControl* getSequenceControl();
+
+
     public:
         
         /**
@@ -325,8 +318,7 @@ namespace osgEarth
                 _empty        ( rhs._empty ),
                 _cacheBinId   ( rhs._cacheBinId ),
                 _sourceName   ( rhs._sourceName ),
-                _sourceDriver ( rhs._sourceDriver ),
-                _maxDataLevel ( rhs._maxDataLevel ),
+                _sourceDriver ( rhs._sourceDriver ),                
                 _sourceProfile( rhs._sourceProfile ),
                 _cacheProfile ( rhs._cacheProfile ) { }
 
@@ -335,8 +327,7 @@ namespace osgEarth
                 _empty = conf.empty();
                 conf.getIfSet   ( "cachebin_id",    _cacheBinId );
                 conf.getIfSet   ( "source_name",    _sourceName );
-                conf.getIfSet   ( "source_driver",  _sourceDriver );
-                conf.getIfSet   ( "max_data_level", _maxDataLevel );
+                conf.getIfSet   ( "source_driver",  _sourceDriver );                
                 conf.getObjIfSet( "source_profile", _sourceProfile );
                 conf.getObjIfSet( "cache_profile",  _cacheProfile );
             }
@@ -346,8 +337,7 @@ namespace osgEarth
                 Config conf( "osgearth_terrainlayer_cachebin" );
                 conf.addIfSet   ( "cachebin_id",    _cacheBinId );
                 conf.addIfSet   ( "source_name",    _sourceName );
-                conf.addIfSet   ( "source_driver",  _sourceDriver );
-                conf.addIfSet   ( "max_data_level", _maxDataLevel );
+                conf.addIfSet   ( "source_driver",  _sourceDriver );                
                 conf.addObjIfSet( "source_profile", _sourceProfile );
                 conf.addObjIfSet( "cache_profile",  _cacheProfile );
                 return conf;
@@ -356,8 +346,7 @@ namespace osgEarth
             bool                     _empty;
             optional<std::string>    _cacheBinId;
             optional<std::string>    _sourceName;
-            optional<std::string>    _sourceDriver;
-            optional<unsigned>       _maxDataLevel;
+            optional<std::string>    _sourceDriver;            
             optional<ProfileOptions> _sourceProfile;
             optional<ProfileOptions> _cacheProfile;
         };
diff --git a/src/osgEarth/TerrainLayer.cpp b/src/osgEarth/TerrainLayer.cpp
index fa6528e..8f9143a 100644
--- a/src/osgEarth/TerrainLayer.cpp
+++ b/src/osgEarth/TerrainLayer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
 #include <osgEarth/TileSource>
 #include <osgEarth/Registry>
 #include <osgEarth/StringUtils>
+#include <osgEarth/TimeControl>
 #include <osgEarth/URI>
 #include <osgDB/WriteFile>
 #include <osg/Version>
@@ -84,8 +85,7 @@ TerrainLayerOptions::getConfig( bool isolate ) const
     conf.updateIfSet( "loading_weight", _loadingWeight );
     conf.updateIfSet( "enabled", _enabled );
     conf.updateIfSet( "visible", _visible );
-    conf.updateIfSet( "edge_buffer_ratio", _edgeBufferRatio);
-    conf.updateIfSet( "max_data_level", _maxDataLevel);
+    conf.updateIfSet( "edge_buffer_ratio", _edgeBufferRatio);    
     conf.updateIfSet( "reprojected_tilesize", _reprojectedTileSize);
 
     conf.updateIfSet( "vdatum", _vertDatum );
@@ -113,8 +113,7 @@ TerrainLayerOptions::fromConfig( const Config& conf )
     conf.getIfSet( "loading_weight", _loadingWeight );
     conf.getIfSet( "enabled", _enabled );
     conf.getIfSet( "visible", _visible );
-    conf.getIfSet( "edge_buffer_ratio", _edgeBufferRatio);
-    conf.getIfSet( "max_data_level", _maxDataLevel);
+    conf.getIfSet( "edge_buffer_ratio", _edgeBufferRatio);    
     conf.getIfSet( "reprojected_tilesize", _reprojectedTileSize);
 
     conf.getIfSet( "vdatum", _vertDatum );
@@ -329,27 +328,6 @@ TerrainLayer::getProfile() const
     return _profile.get();
 }
 
-unsigned int
-TerrainLayer::getMaxDataLevel() const
-{
-    //Try the setting first
-
-    if ( _runtimeOptions->maxDataLevel().isSet() )
-    {
-        return _runtimeOptions->maxDataLevel().get();
-    }
-
-    //Try the TileSource
-    TileSource* ts = getTileSource();
-    if ( ts )
-    {
-        return ts->getMaxDataLevel();
-    }
-
-    //Just default
-    return 20;
-}
-
 unsigned
 TerrainLayer::getTileSize() const
 {
@@ -438,10 +416,7 @@ TerrainLayer::getCacheBin( const Profile* profile, const std::string& binId )
                 {
                     // in cacheonly mode, create a profile from the first cache bin accessed
                     // (they SHOULD all be the same...)
-                    _profile = Profile::create( *meta._sourceProfile );
-
-                    // copy the max data level from the cache
-                    _runtimeOptions->maxDataLevel() = *meta._maxDataLevel;
+                    _profile = Profile::create( *meta._sourceProfile );                    
                 }
             }
 
@@ -453,8 +428,7 @@ TerrainLayer::getCacheBin( const Profile* profile, const std::string& binId )
                 {
                     // no existing metadata; create some.
                     meta._cacheBinId    = binId;
-                    meta._sourceName    = this->getName();
-                    meta._maxDataLevel  = getMaxDataLevel();
+                    meta._sourceName    = this->getName();                    
                     meta._sourceDriver  = getTileSource()->getOptions().getDriver();
                     meta._sourceProfile = getProfile()->toProfileOptions();
                     meta._cacheProfile  = profile->toProfileOptions();
@@ -681,3 +655,8 @@ TerrainLayer::storeProxySettings(osgDB::Options* opt)
     }
 }
 
+SequenceControl*
+TerrainLayer::getSequenceControl()
+{
+    return dynamic_cast<SequenceControl*>( getTileSource() );
+}
diff --git a/src/osgEarth/TerrainOptions b/src/osgEarth/TerrainOptions
index c0c3e68..03dc03e 100644
--- a/src/osgEarth/TerrainOptions
+++ b/src/osgEarth/TerrainOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
 namespace osgEarth
 {
     /**
+     * @deprecated
      * The LoadingPolicy configures how the terrain engine loads map data.
      */
     class OSGEARTH_EXPORT LoadingPolicy
@@ -54,7 +55,6 @@ namespace osgEarth
 
     public:
         LoadingPolicy( const Config& conf =Config() );
-        LoadingPolicy( const Mode& mode );
         virtual ~LoadingPolicy() { }
 
     public: // Configrable
@@ -145,18 +145,10 @@ namespace osgEarth
          * Default = 6.0.
          */
         optional<float>& minTileRangeFactor() { return _minTileRangeFactor; }
-        const optional<float>& minTileRangeFactor() const { return _minTileRangeFactor; }        
-
-        /**
-         * Whether to apply the default layer combination logic using TexEnvCombine.
-         * Set this to false if you want to assume all control of how layers are combined.
-         * This only applies when the LayeringTechnique is MULTITEXTURE.
-         * Default = true.
-         */
-        optional<bool>& combineLayers() { return _combineLayers; }
-        const optional<bool>& combineLayers() const { return _combineLayers; }
+        const optional<float>& minTileRangeFactor() const { return _minTileRangeFactor; }
 
         /**
+         * @deprecated
          * Properties associated with the tile loading subsystem.
          */
         optional<LoadingPolicy>& loadingPolicy() { return _loadingPolicy; }
@@ -181,6 +173,7 @@ namespace osgEarth
         const optional<bool>& clusterCulling() const { return _clusterCulling; }
 
         /**
+         * @deprecated - will either go away or be moved into the Quadtree terrain engine
          * Available techniques for compositing image layers at runtime.
          */
         enum CompositingTechnique
@@ -194,6 +187,7 @@ namespace osgEarth
         };
 
         /**
+         * @deprecated - will either go away or be moved into the Quadtree terrain engine
          * The texture composition technique
          */
         optional<CompositingTechnique>& compositingTechnique() { return _compositingTech; }
@@ -220,6 +214,14 @@ namespace osgEarth
         const optional<unsigned>& minLOD() const { return _minLOD; }
 
         /**
+         * The lowest LOD to display. By default, the terrain begins at LOD 0. Set this
+         * to start the terrain tile mesh at a higher LOD. Don't set this TOO high though
+         * or you will run into memory problems.
+         */
+        optional<unsigned>& firstLOD() { return _firstLOD; }
+        const optional<unsigned>& firstLOD() const { return _firstLOD; }
+
+        /**
          * Whether to explicity enable or disable GL lighting on the map node.
          */
         optional<bool>& enableLighting() { return _enableLighting; }
@@ -293,6 +295,7 @@ namespace osgEarth
         optional<CompositingTechnique> _compositingTech;
         optional<unsigned> _minLOD;
         optional<unsigned> _maxLOD;
+        optional<unsigned> _firstLOD;
         optional<bool> _enableLighting;
         optional<float> _attenuationDistance;
         optional<bool> _lodBlending;
diff --git a/src/osgEarth/TerrainOptions.cpp b/src/osgEarth/TerrainOptions.cpp
index 0788469..1acf939 100644
--- a/src/osgEarth/TerrainOptions.cpp
+++ b/src/osgEarth/TerrainOptions.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -95,6 +95,7 @@ _loadingPolicy( LoadingPolicy() ),
 _compositingTech( COMPOSITING_AUTO ),
 _maxLOD( 23 ),
 _minLOD( 0 ),
+_firstLOD( 0 ),
 _enableLighting( false ),
 _attenuationDistance( 1000000 ),
 _lodBlending( false ),
@@ -125,9 +126,9 @@ TerrainOptions::getConfig() const
     conf.updateObjIfSet( "loading_policy", _loadingPolicy );
     conf.updateIfSet( "vertical_scale", _verticalScale );
     conf.updateIfSet( "min_tile_range_factor", _minTileRangeFactor );    
-    conf.updateIfSet( "combine_layers", _combineLayers );
     conf.updateIfSet( "max_lod", _maxLOD );
     conf.updateIfSet( "min_lod", _minLOD );
+    conf.updateIfSet( "first_lod", _firstLOD );
     conf.updateIfSet( "lighting", _enableLighting );
     conf.updateIfSet( "attenuation_distance", _attenuationDistance );
     conf.updateIfSet( "lod_transition_time", _lodTransitionTimeSeconds );
@@ -172,9 +173,9 @@ TerrainOptions::fromConfig( const Config& conf )
     conf.getObjIfSet( "loading_policy", _loadingPolicy );
     conf.getIfSet( "vertical_scale", _verticalScale );
     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 );
+    conf.getIfSet( "first_lod", _firstLOD );
     conf.getIfSet( "lighting", _enableLighting );
     conf.getIfSet( "attenuation_distance", _attenuationDistance );
     conf.getIfSet( "lod_transition_time", _lodTransitionTimeSeconds );
diff --git a/src/osgEarth/TextureCompositor b/src/osgEarth/TextureCompositor
index 509ee19..c55d4d7 100644
--- a/src/osgEarth/TextureCompositor
+++ b/src/osgEarth/TextureCompositor
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TextureCompositor.cpp b/src/osgEarth/TextureCompositor.cpp
old mode 100644
new mode 100755
index 960b1bc..ce03685
--- a/src/osgEarth/TextureCompositor.cpp
+++ b/src/osgEarth/TextureCompositor.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -121,7 +121,7 @@ TextureLayout::assignPrimarySlot( ImageLayer* layer, int orderIndex )
 
 
 
-    OE_INFO << LC << "Allocated SLOT " << slot << "; primary slot for layer \"" << layer->getName() << "\"" << std::endl;
+    OE_DEBUG << LC << "Allocated SLOT " << slot << "; primary slot for layer \"" << layer->getName() << "\"" << std::endl;
 }
 
 void
@@ -331,10 +331,10 @@ TextureCompositor::reserveTextureImageUnit( int& out_unit )
         return false;
     }
 
-    else // multipass... all image layers are locked at unit 0
+    else // multipass or USER .. just simple reservations.
     {
         // search for an unused unit.
-        for( unsigned i=1; i<maxUnits; ++i ) // start at 1 because unit 0 is always reserved
+        for( unsigned i=0; i<maxUnits; ++i )
         {
             if ( _reservedUnits.find( i ) == _reservedUnits.end() )
             {
@@ -440,7 +440,7 @@ TextureCompositor::requiresUnitTextureSpace() const
 bool
 TextureCompositor::usesShaderComposition() const
 {
-    return _impl.valid() ? _impl->usesShaderComposition() : false;
+    return _impl.valid() ? _impl->usesShaderComposition() : true;
 }
 
 void
diff --git a/src/osgEarth/TextureCompositorMulti b/src/osgEarth/TextureCompositorMulti
index 3fb845f..fb279c3 100644
--- a/src/osgEarth/TextureCompositorMulti
+++ b/src/osgEarth/TextureCompositorMulti
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TextureCompositorMulti.cpp b/src/osgEarth/TextureCompositorMulti.cpp
old mode 100644
new mode 100755
index b1cd1ad..075a658
--- a/src/osgEarth/TextureCompositorMulti.cpp
+++ b/src/osgEarth/TextureCompositorMulti.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -42,7 +42,7 @@ namespace
         return Stringify() << "osgearth_tex" << slot;
     }
 
-    static osg::Shader*
+    static std::string
     s_createTextureVertexShader( const TextureLayout& layout, bool blending )
     {
         std::stringstream buf;
@@ -67,7 +67,7 @@ namespace
             buf << "uniform mat4 osgearth_TexBlendMatrix[" << Registry::capabilities().getMaxGPUTextureCoordSets() << "]; \n";
         }
 
-        buf << "void osgearth_vert_setupColoring() \n"
+        buf << "void oe_multicomp_vertex(inout vec4 VertexMODEL) \n"
             << "{ \n"
             << "    osg_FrontColor = gl_Color; \n"
             << "    osg_FrontSecondaryColor = vec4(0.0); \n";
@@ -99,10 +99,11 @@ namespace
 
         std::string str;
         str = buf.str();
-        return new osg::Shader( osg::Shader::VERTEX, str );
+        return str;
+        //return new osg::Shader( osg::Shader::VERTEX, str );
     }
 
-    static osg::Shader*
+    static std::string
     s_createTextureFragShaderFunction( const TextureLayout& layout, int maxSlots, bool blending, float fadeInDuration )
     {
         const TextureLayout::RenderOrderVector& order = layout.getRenderOrder();
@@ -149,7 +150,7 @@ namespace
         }
 
         // the main texturing function:
-        buf << "void osgearth_frag_applyColoring( inout vec4 color ) \n"
+        buf << "void oe_multicomp_fragment( inout vec4 color ) \n"
             << "{ \n"
             << "    vec3 color3 = color.rgb; \n"
             << "    vec4 texel; \n"
@@ -219,8 +220,9 @@ namespace
 
         std::string str;
         str = buf.str();
+        return str;
         //OE_INFO << std::endl << str;
-        return new osg::Shader( osg::Shader::FRAGMENT, str );
+        //return new osg::Shader( osg::Shader::FRAGMENT, str );
     }
 }
 
@@ -406,15 +408,27 @@ TextureCompositorMultiTexture::updateMasterStateSet(osg::StateSet*       stateSe
         // 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_vert_setupColoring",
+        vp->setFunction(
+            "oe_multicomp_vertex",
             s_createTextureVertexShader(layout, hasBlending),
-            osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+            ShaderComp::LOCATION_VERTEX_MODEL,
+            0.0 );
 
-        vp->setShader(
-            "osgearth_frag_applyColoring",
+        vp->setFunction(
+            "oe_multicomp_fragment",
             s_createTextureFragShaderFunction(layout, maxUnits, hasBlending, _lodTransitionTime),
-            osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+            ShaderComp::LOCATION_FRAGMENT_COLORING,
+            0.0 );
+
+        //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
diff --git a/src/osgEarth/TextureCompositorTexArray b/src/osgEarth/TextureCompositorTexArray
index 6279dcb..81f14f9 100644
--- a/src/osgEarth/TextureCompositorTexArray
+++ b/src/osgEarth/TextureCompositorTexArray
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TextureCompositorTexArray.cpp b/src/osgEarth/TextureCompositorTexArray.cpp
index 4d02923..589abaa 100644
--- a/src/osgEarth/TextureCompositorTexArray.cpp
+++ b/src/osgEarth/TextureCompositorTexArray.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/ThreadingUtils b/src/osgEarth/ThreadingUtils
index db5e46a..7463799 100644
--- a/src/osgEarth/ThreadingUtils
+++ b/src/osgEarth/ThreadingUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -468,6 +468,65 @@ namespace osgEarth { namespace Threading
 
     private:
         std::map<KEY,osg::observer_ptr<DATA> >    _data;
+        osgEarth::Threading::ReadWriteMutex       _mutex;
+    };
+
+
+    /** Template for thread safe observer set */
+    template<typename T>
+    struct ThreadSafeObserverSet
+    {
+        typedef void (*Functor)(T*);
+        typedef void (*ConstFunctor)(const T*);
+
+        void iterate( const Functor& f )
+        {
+            osgEarth::Threading::ScopedWriteLock lock(_mutex);
+            for( typename std::set<T>::iterator i = _data.begin(); i != _data.end(); )
+            {
+                if ( i->valid() )
+                {
+                    f( i->get() );
+                    ++i;
+                }
+                else
+                {
+                    i = _data.erase( i );
+                }
+            }
+        }
+
+        void iterate( const ConstFunctor& f )
+        {
+            osgEarth::Threading::ScopedReadLock lock(_mutex);
+            for( typename std::set<T>::iterator i = _data.begin(); i != _data.end(); )
+            {
+                if ( i->valid() )
+                {
+                    f( i->get() );
+                    ++i;
+                }
+                else
+                {
+                    i = _data.erase( i );
+                }
+            }
+        }
+
+        void insert(T* obj)
+        {
+            osgEarth::Threading::ScopedWriteLock lock(_mutex);
+            _data.insert( obj );
+        }
+
+        void remove(T* obj)
+        {
+            osgEarth::Threading::ScopedWriteLock lock(_mutex);
+            _data.erase( obj );
+        }
+
+    private:
+        std::set<osg::observer_ptr<T> >      _data;
         osgEarth::Threading::ReadWriteMutex  _mutex;
     };
 
diff --git a/src/osgEarth/ThreadingUtils.cpp b/src/osgEarth/ThreadingUtils.cpp
index a50111d..ae323d0 100644
--- a/src/osgEarth/ThreadingUtils.cpp
+++ b/src/osgEarth/ThreadingUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -31,12 +31,14 @@ using namespace osgEarth::Threading;
 
 unsigned osgEarth::Threading::getCurrentThreadId()
 {
- /*   OpenThreads::Thread* t = OpenThreads::Thread::CurrentThread();
-    return t ? t->getThreadId() : 0u;*/
-
+  /*   OpenThreads::Thread* t = OpenThreads::Thread::CurrentThread();
+   return t ? t->getThreadId() : 0u;*/
+  
 #ifdef _WIN32
-        return (unsigned)::GetCurrentThreadId();
+  return (unsigned)::GetCurrentThreadId();
+#elif __APPLE__
+  return ::syscall(SYS_thread_selfid);
 #else
-        return (unsigned)::syscall(SYS_gettid);
+  return (unsigned)::syscall(SYS_gettid);
 #endif
 }
diff --git a/src/osgEarth/TileKey b/src/osgEarth/TileKey
index 588d88c..d0acd93 100644
--- a/src/osgEarth/TileKey
+++ b/src/osgEarth/TileKey
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TileKey.cpp b/src/osgEarth/TileKey.cpp
index a6360f5..6355fa5 100644
--- a/src/osgEarth/TileKey.cpp
+++ b/src/osgEarth/TileKey.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/TileSource b/src/osgEarth/TileSource
index a629d43..5d9ead7 100644
--- a/src/osgEarth/TileSource
+++ b/src/osgEarth/TileSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -278,23 +278,7 @@ namespace osgEarth
          * Gets the nodata max value
          */
         virtual float getNoDataMaxValue() {
-            return _options.noDataMaxValue().value(); }
-
-        /**
-         * Gets the maximum level of detail available from the tile source. Unlike
-         * getMaxLevel(), which reports the maximum level at which to use this tile
-         * source in a Map, this method reports the maximum level for which the
-         * tile source is able to return data.
-         */
-        virtual unsigned int getMaxDataLevel() const;
-
-        /**
-         * Gets the minimum level of detail available from the tile source. Unlike
-         * getMinLevel(), which reports the minimum level at which to use this tile
-         * source in a Map, this method reports the minimum level for which the
-         * tile source is able to return data.
-         */
-        virtual unsigned int getMinDataLevel() const;
+            return _options.noDataMaxValue().value(); }        
 
         /**
          * Gets the preferred extension for this TileSource
diff --git a/src/osgEarth/TileSource.cpp b/src/osgEarth/TileSource.cpp
index dd64a4f..2fd33a8 100644
--- a/src/osgEarth/TileSource.cpp
+++ b/src/osgEarth/TileSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -219,13 +219,15 @@ _status ( Status::Error("Not initialized") )
 {
     this->setThreadSafeRefUnref( true );
 
-    if ( *options.L2CacheSize() > 0 )
+    int l2CacheSize = 0;
+    char const* l2env = ::getenv( "OSGEARTH_L2_CACHE_SIZE" );
+    if ( l2env )
     {
-        _memCache = new MemCache( *options.L2CacheSize() );
+        l2CacheSize = as<int>( std::string(l2env), 0 );
     }
-    else
+    else if ( *options.L2CacheSize() > 0 )
     {
-        OE_INFO << LC << "L2 Cache disabled" << std::endl;
+        _memCache = new MemCache( *options.L2CacheSize() );
     }
 
     if (_options.blacklistFilename().isSet())
@@ -398,40 +400,6 @@ TileSource::getProfile() const
     return _profile.get();
 }
 
-unsigned
-TileSource::getMaxDataLevel() const
-{
-    optional<unsigned> maxDataLevel;
-
-    for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr)
-    {
-        if ( itr->maxLevel().isSet() && itr->maxLevel() > *maxDataLevel )
-        {
-            maxDataLevel = itr->maxLevel().get();
-        }
-    }
-
-    // return "23" if no max is found
-    return maxDataLevel.isSet() ? *maxDataLevel : 23u;
-}
-
-unsigned
-TileSource::getMinDataLevel() const
-{
-    optional<unsigned> minDataLevel;
-
-    for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr)
-    {
-        if ( itr->minLevel().isSet() && itr->minLevel() < *minDataLevel )
-        {
-            minDataLevel = itr->minLevel().get();
-        }
-    }
-
-    return minDataLevel.isSet() ? *minDataLevel : 0;
-}
-
-
 bool
 TileSource::hasDataAtLOD( unsigned lod ) const
 {
diff --git a/src/osgEarth/TimeControl b/src/osgEarth/TimeControl
new file mode 100644
index 0000000..8a5c0fa
--- /dev/null
+++ b/src/osgEarth/TimeControl
@@ -0,0 +1,67 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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_TIME_CONTROL_H
+#define OSGEARTH_TIME_CONTROL_H 1
+
+#include <osgEarth/Common>
+#include <osg/FrameStamp>
+#include <vector>
+
+namespace osgEarth
+{
+    /**
+     * Information about one of the frames in a sequence.
+     */
+    struct SequenceFrameInfo
+    {
+        std::string timeIdentifier;
+    };
+
+
+    /**
+     * Interface to an object that is subject to time controls.
+     * Pure virtual.
+     */
+    class SequenceControl
+    {
+    public:
+        /** Whether the implementation supports these methods */
+        virtual bool supportsSequenceControl() const =0;
+
+        /** Starts playback */
+        virtual void playSequence() =0;
+
+        /** Stops playback */
+        virtual void pauseSequence() =0;
+
+        /** Seek to a specific frame */
+        virtual void seekToSequenceFrame(unsigned frame) =0;
+
+        /** Whether the object is in playback mode */
+        virtual bool isSequencePlaying() const =0;
+
+        /** Gets data about the current frame in the sequence */
+        virtual const std::vector<SequenceFrameInfo>& getSequenceFrameInfo() const =0;
+
+        /** Gets the index of the current frame. */
+        virtual int getCurrentSequenceFrameIndex( const osg::FrameStamp* ) const =0;
+    };
+}
+
+#endif // OSGEARTH_TIME_CONTROL_H
diff --git a/src/osgEarthDrivers/kml/KML_Schema.cpp b/src/osgEarth/TimeControl.cpp
similarity index 88%
copy from src/osgEarthDrivers/kml/KML_Schema.cpp
copy to src/osgEarth/TimeControl.cpp
index 939c1e6..17daa32 100644
--- a/src/osgEarthDrivers/kml/KML_Schema.cpp
+++ b/src/osgEarth/TimeControl.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,6 +16,6 @@
  * 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 "KML_Schema"
+#include <osgEarth/TimeControl>
 
-using namespace osgEarth_kml;
+using namespace osgEarth;
diff --git a/src/osgEarth/TraversalData b/src/osgEarth/TraversalData
new file mode 100644
index 0000000..9e6b7f1
--- /dev/null
+++ b/src/osgEarth/TraversalData
@@ -0,0 +1,79 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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_TRAVERSAL_DATA_H
+#define OSGEARTH_TRAVERSAL_DATA_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/ThreadingUtils>
+#include <osg/NodeVisitor>
+#include <osg/observer_ptr>
+#include <map>
+
+namespace osgEarth
+{
+    class MapNode;
+
+    class TraversalData : public osg::Referenced
+    {
+    public:
+        typedef std::map<void*, osg::ref_ptr<osg::Referenced> > UserDataMap;
+
+        template<typename T> T& getOrCreate(void* key)
+        {
+            UserDataMap::iterator i = _userData.find( key );
+            if ( i != _userData.end() )
+                return *static_cast<T*>( i->second.get() );
+            T* t = new T();
+            _userData[key] = t;
+            return *t;
+        }
+
+    public:
+        TraversalData() { }
+        static TraversalData* get(osg::NodeVisitor& nv)
+        {
+            return dynamic_cast<TraversalData*>( nv.getUserData() );
+        }
+
+    protected:
+        UserDataMap _userData;
+    };
+
+
+    /**
+     * Per-camera data for MapNode culling traversals.
+     */
+    class OSGEARTH_EXPORT MapNodeCullData : public osg::Referenced
+    {
+    public:
+        MapNodeCullData();
+
+    public:
+        osg::observer_ptr<class MapNode>  _mapNode;
+        osg::ref_ptr<osg::StateSet> _stateSet;
+        osg::ref_ptr<osg::Uniform>  _windowScaleMatrix;
+        double                      _cameraAltitude;
+
+    protected:
+        virtual ~MapNodeCullData() { }
+    };
+
+} // namespace osgEarth
+
+#endif // OSGEARTH_TRAVERSAL_DATA_H
diff --git a/src/osgEarth/MaskNode.cpp b/src/osgEarth/TraversalData.cpp
similarity index 65%
copy from src/osgEarth/MaskNode.cpp
copy to src/osgEarth/TraversalData.cpp
index 475f533..e474816 100644
--- a/src/osgEarth/MaskNode.cpp
+++ b/src/osgEarth/TraversalData.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,21 +16,19 @@
  * 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/MaskNode>
+#include <osgEarth/TraversalData>
 
 using namespace osgEarth;
 
-MaskNode::MaskNode()
+MapNodeCullData::MapNodeCullData()
 {
-    //nop
-}
-
-MaskNode::MaskNode( const MaskNode& rhs, const osg::CopyOp& op )
-: osg::Group( rhs, op )
-{
-    //nop
-}
-
-
+    _stateSet = new osg::StateSet();
 
+    _windowScaleMatrix = new osg::Uniform(osg::Uniform::FLOAT_MAT3, "oe_WindowScaleMatrix");
+    osg::Matrix3 identity;
+    identity.makeIdentity();
+    _windowScaleMatrix->set( identity );
+    _stateSet->addUniform( _windowScaleMatrix.get() );
 
+    _cameraAltitude = 0.0;
+}
diff --git a/src/osgEarth/URI b/src/osgEarth/URI
index c8ad898..e2b98ca 100644
--- a/src/osgEarth/URI
+++ b/src/osgEarth/URI
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/URI.cpp b/src/osgEarth/URI.cpp
index d90c9fe..afcec01 100644
--- a/src/osgEarth/URI.cpp
+++ b/src/osgEarth/URI.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -362,10 +362,10 @@ namespace
             URIResultCache* memCache = URIResultCache::from( localOptions );
             if ( memCache )
             {
-                URIResultCache::Record r = memCache->get( uri );
-                if ( r.valid() )
+                URIResultCache::Record rec;
+                if ( memCache->get(uri, rec) )
                 {
-                    result = r.value();
+                    result = rec.value();
                 }
             }
 
@@ -425,10 +425,15 @@ namespace
                     // not in the cache, so proceed to read it from the network.
                     if ( result.empty() )
                     {
+                        // Need to do this to support nested PLODs and Proxynodes.
+                        osg::ref_ptr<osgDB::Options> remoteOptions =
+                            Registry::instance()->cloneOrCreateOptions( localOptions );
+                        remoteOptions->getDatabasePathList().push_front( osgDB::getFilePath(uri.full()) );
+
                         // try to use the callback if it's set. Callback ignores the caching policy.
                         if ( cb )
                         {                
-                            result = reader.fromCallback( cb, uri.full(), localOptions );
+                            result = reader.fromCallback( cb, uri.full(), remoteOptions.get() );
 
                             if ( result.code() != ReadResult::RESULT_NOT_IMPLEMENTED )
                             {
@@ -442,7 +447,7 @@ namespace
                             // still no data, go to the source:
                             if ( result.empty() && cp->usage() != CachePolicy::USAGE_CACHE_ONLY )
                             {
-                                result = reader.fromHTTP( uri.full(), localOptions, progress );
+                                result = reader.fromHTTP( uri.full(), remoteOptions.get(), progress );
                             }
 
                             // write the result to the cache if possible:
diff --git a/src/osgEarth/Units b/src/osgEarth/Units
index bc5abf2..b01b9c3 100644
--- a/src/osgEarth/Units
+++ b/src/osgEarth/Units
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -24,6 +24,8 @@
 
 namespace osgEarth
 {
+    class Registry;
+
     class OSGEARTH_EXPORT Units
     {
     public:
@@ -68,12 +70,29 @@ namespace osgEarth
         static const Units DATA_MILES_PER_HOUR;
         static const Units KNOTS;
 
-        enum Type { TYPE_LINEAR, TYPE_ANGULAR, TYPE_TEMPORAL, TYPE_SPEED, TYPE_INVALID };
+        // screen
+        static const Units PIXELS;
+
+        // unit types.
+        enum Type
+        { 
+            TYPE_LINEAR, 
+            TYPE_ANGULAR, 
+            TYPE_TEMPORAL, 
+            TYPE_SPEED, 
+            TYPE_SCREEN_SIZE,
+            TYPE_INVALID
+        };
 
     public:
 
         static bool parse( const std::string& input, Units& output );
 
+        // parses a value+units string (like "15cm" or "24px")
+        static bool parse( const std::string& input, double& out_value, Units& out_units, const Units& defaultUnits );
+        static bool parse( const std::string& input, float& out_value, Units& out_units, const Units& defaultUnits );
+        static bool parse( const std::string& input, int& out_value, Units& out_units, const Units& defaultUnits );
+
         static bool convert( const Units& from, const Units& to, double input, double& output ) {
             if ( canConvert(from, to) ) {
                 if ( from._type == TYPE_LINEAR || from._type == TYPE_ANGULAR || from._type == TYPE_TEMPORAL )
@@ -127,9 +146,11 @@ namespace osgEarth
 
         bool isSpeed() const { return _type == TYPE_SPEED; }
 
+        bool isScreenSize() const { return _type == TYPE_SCREEN_SIZE; }
+
     public:
 
-        // Make a new unit definition (LINEAR, ANGULAR, TEMPORAL)
+        // Make a new unit definition (LINEAR, ANGULAR, TEMPORAL, SCREEN)
         Units( const std::string& name, const std::string& abbr, const Type& type, double toBase );
 
         // Maks a new unit definition (SPEED)
@@ -153,6 +174,11 @@ namespace osgEarth
         double _toBase;
         const Units* _distance;
         const Units* _time;
+        
+
+        // called by Registry to register system units
+        static void registerAll(class Registry* registry);
+        friend class Registry;
     };
 
     struct Linear;
@@ -274,6 +300,13 @@ namespace osgEarth
         Speed(const Config& conf) : qualified_double<Speed>(conf, Units::METERS_PER_SECOND) { }
     };
 
+    struct ScreenSize : public qualified_double<ScreenSize> {
+        ScreenSize() : qualified_double<ScreenSize>(0, Units::PIXELS) { }
+        ScreenSize(double value) : qualified_double<ScreenSize>(value, Units::PIXELS) { }
+        ScreenSize(double value, const Units& units) : qualified_double<ScreenSize>(value, units) { }
+        ScreenSize(const Config& conf) : qualified_double<ScreenSize>(conf, Units::PIXELS) { }
+    };
+
 }
 
 #endif // OSGEARTH_UNITS_H
diff --git a/src/osgEarth/Units.cpp b/src/osgEarth/Units.cpp
index c41b2bb..e528ff4 100644
--- a/src/osgEarth/Units.cpp
+++ b/src/osgEarth/Units.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,9 +20,66 @@
 #include <osgEarth/Units>
 #include <osgEarth/Registry>
 #include <osg/Math>
+#include <algorithm>
+#include <cctype>
 
 using namespace osgEarth;
 
+//------------------------------------------------------------------------
+
+namespace
+{
+    template<typename T>
+    bool
+    parseValueAndUnits(const std::string& input, 
+                       T&                 out_value, 
+                       Units&             out_units,
+                       const Units&       defaultUnits )
+    {
+        if ( input.empty() )
+            return false;
+
+        std::string valueStr, unitsStr;
+
+        std::string::const_iterator i = std::find_if( input.begin(), input.end(), ::isalpha );
+        if ( i == input.end() )
+        {
+            // to units found; use default
+            out_units = defaultUnits;
+            out_value = as<T>(input, (T)0.0);
+            return true;
+        }
+
+        else
+        {
+            valueStr = std::string( input.begin(), i );
+            unitsStr = std::string( i, input.end() );
+
+            if ( !valueStr.empty() )
+            {
+                out_value = as<T>(valueStr, (T)0);
+            }
+
+            if ( !unitsStr.empty() )
+            {
+                Units units;
+                if ( Units::parse(unitsStr, units) )
+                    out_units = units;
+            }
+            else
+            {
+                out_units = defaultUnits;
+            }
+
+            return !valueStr.empty() && !unitsStr.empty();
+        }
+    }
+}
+
+//------------------------------------------------------------------------
+
+
+
 Units::Units( const std::string& name, const std::string& abbr, const Units::Type& type, double toBase ) :
 _name  ( name ),
 _abbr  ( abbr ),
@@ -54,105 +111,103 @@ Units::parse( const std::string& name, Units& output )
     return false;
 }
 
-const Units Units::CENTIMETERS       ( "centimeters",    "cm",  Units::TYPE_LINEAR, 0.01 ); 
-OSGEARTH_REGISTER_UNITS( CENTIMETERS, &Units::CENTIMETERS );
+bool
+Units::parse( const std::string& input, float& out_value, Units& out_units, const Units& defaultUnits )
+{
+    return parseValueAndUnits(input, out_value, out_units, defaultUnits);
+}
 
-const Units Units::FEET              ( "feet",           "ft",  Units::TYPE_LINEAR, 0.3048 );
-OSGEARTH_REGISTER_UNITS( FEET, &Units::FEET );
+bool
+Units::parse( const std::string& input, double& out_value, Units& out_units, const Units& defaultUnits )
+{
+    return parseValueAndUnits(input, out_value, out_units, defaultUnits);
+}
 
-const Units Units::FEET_US_SURVEY    ( "feet(us)",       "ft",  Units::TYPE_LINEAR, 12.0/39.37 );
-OSGEARTH_REGISTER_UNITS( FEET_US_SURVEY, &Units::FEET_US_SURVEY );
+bool
+Units::parse( const std::string& input, int& out_value, Units& out_units, const Units& defaultUnits )
+{
+    return parseValueAndUnits(input, out_value, out_units, defaultUnits);
+}
 
-const Units Units::KILOMETERS        ( "kilometers",     "km",  Units::TYPE_LINEAR, 1000.0 );
-OSGEARTH_REGISTER_UNITS( KILOMETERS, &Units::KILOMETERS );
+void
+Units::registerAll(Registry* r)
+{
+    r->registerUnits( &Units::CENTIMETERS );
+    r->registerUnits( &Units::FEET );
+    r->registerUnits( &Units::FEET_US_SURVEY );
+    r->registerUnits( &Units::KILOMETERS );
+    r->registerUnits( &Units::METERS );
+    r->registerUnits( &Units::MILES );
+    r->registerUnits( &Units::MILLIMETERS );
+    r->registerUnits( &Units::YARDS );
+    r->registerUnits( &Units::NAUTICAL_MILES );
+    r->registerUnits( &Units::DATA_MILES );
+    r->registerUnits( &Units::INCHES );
+    r->registerUnits( &Units::FATHOMS );
+    r->registerUnits( &Units::KILOFEET );
+    r->registerUnits( &Units::KILOYARDS );
+
+    r->registerUnits( &Units::DEGREES );
+    r->registerUnits( &Units::RADIANS );
+    r->registerUnits( &Units::BAM );
+    r->registerUnits( &Units::NATO_MILS );
+
+    r->registerUnits( &Units::DAYS );
+    r->registerUnits( &Units::HOURS );
+    r->registerUnits( &Units::MICROSECONDS );
+    r->registerUnits( &Units::MILLISECONDS );
+    r->registerUnits( &Units::MINUTES );
+    r->registerUnits( &Units::SECONDS );
+    r->registerUnits( &Units::WEEKS );
+
+    r->registerUnits( &Units::FEET_PER_SECOND );
+    r->registerUnits( &Units::YARDS_PER_SECOND );
+    r->registerUnits( &Units::METERS_PER_SECOND );
+    r->registerUnits( &Units::KILOMETERS_PER_SECOND );
+    r->registerUnits( &Units::KILOMETERS_PER_HOUR );
+    r->registerUnits( &Units::MILES_PER_HOUR );
+    r->registerUnits( &Units::DATA_MILES_PER_HOUR );
+    r->registerUnits( &Units::KNOTS );
+
+    r->registerUnits( &Units::PIXELS );
+}
 
-const Units Units::METERS            ( "meters",         "m",   Units::TYPE_LINEAR, 1.0 );
-OSGEARTH_REGISTER_UNITS( METERS, &Units::METERS );
 
+const Units Units::CENTIMETERS       ( "centimeters",    "cm",  Units::TYPE_LINEAR, 0.01 ); 
+const Units Units::FEET              ( "feet",           "ft",  Units::TYPE_LINEAR, 0.3048 );
+const Units Units::FEET_US_SURVEY    ( "feet(us)",       "ft",  Units::TYPE_LINEAR, 12.0/39.37 );
+const Units Units::KILOMETERS        ( "kilometers",     "km",  Units::TYPE_LINEAR, 1000.0 );
+const Units Units::METERS            ( "meters",         "m",   Units::TYPE_LINEAR, 1.0 );
 const Units Units::MILES             ( "miles",          "mi",  Units::TYPE_LINEAR, 1609.334 );
-OSGEARTH_REGISTER_UNITS( MILES, &Units::MILES );
-
 const Units Units::MILLIMETERS       ( "millimeters",    "mm",  Units::TYPE_LINEAR, 0.001 );
-OSGEARTH_REGISTER_UNITS( MILLIMETERS, &Units::MILLIMETERS );
-
 const Units Units::YARDS             ( "yards",          "yd",  Units::TYPE_LINEAR, 0.9144 );
-OSGEARTH_REGISTER_UNITS( YARDS, &Units::YARDS );
-
 const Units Units::NAUTICAL_MILES    ( "nautical miles", "nm",  Units::TYPE_LINEAR, 1852.0 );
-OSGEARTH_REGISTER_UNITS( NAUTICAL_MILES, &Units::NAUTICAL_MILES );
-
 const Units Units::DATA_MILES        ( "data miles",     "dm",  Units::TYPE_LINEAR, 1828.8 );
-OSGEARTH_REGISTER_UNITS( DATA_MILES, &Units::DATA_MILES );
-
 const Units Units::INCHES            ( "inches",         "in",  Units::TYPE_LINEAR, 0.0254 );
-OSGEARTH_REGISTER_UNITS( INCHES, &Units::INCHES );
-
 const Units Units::FATHOMS           ( "fathoms",        "fm",  Units::TYPE_LINEAR, 1.8288 );
-OSGEARTH_REGISTER_UNITS( FATHOMS, &Units::FATHOMS );
-
 const Units Units::KILOFEET          ( "kilofeet",       "kf",  Units::TYPE_LINEAR, 304.8 );
-OSGEARTH_REGISTER_UNITS( KILOFEET, &Units::KILOFEET );
-
 const Units Units::KILOYARDS         ( "kiloyards",      "kyd", Units::TYPE_LINEAR, 914.4 );
-OSGEARTH_REGISTER_UNITS( KILOYARDS, &Units::KILOYARDS );
-
 
 const Units Units::DEGREES           ( "degrees",        "\xb0",Units::TYPE_ANGULAR, 0.017453292519943295 );
-OSGEARTH_REGISTER_UNITS( DEGREES, &Units::DEGREES );
-
 const Units Units::RADIANS           ( "radians",        "rad", Units::TYPE_ANGULAR, 1.0 );
-OSGEARTH_REGISTER_UNITS( RADIANS, &Units::RADIANS );
-
 const Units Units::BAM               ( "BAM",            "bam", Units::TYPE_ANGULAR, 6.283185307179586476925286766559 );
-OSGEARTH_REGISTER_UNITS( BAM, &Units::BAM );
-
 const Units Units::NATO_MILS         ( "mils",           "mil", Units::TYPE_ANGULAR, 9.8174770424681038701957605727484e-4 );
-OSGEARTH_REGISTER_UNITS( NATO_MILS, &Units::NATO_MILS );
-
 
 const Units Units::DAYS              ( "days",           "d",   Units::TYPE_TEMPORAL, 1.0/86400.0 );
-OSGEARTH_REGISTER_UNITS( DAYS, &Units::DAYS );
-
 const Units Units::HOURS             ( "hours",          "hr",  Units::TYPE_TEMPORAL, 1.0/3600.0 );
-OSGEARTH_REGISTER_UNITS( HOURS, &Units::HOURS );
-
 const Units Units::MICROSECONDS      ( "microseconds",   "us",  Units::TYPE_TEMPORAL, 1000000.0 );
-OSGEARTH_REGISTER_UNITS( MICROSECONDS, &Units::MICROSECONDS );
-
 const Units Units::MILLISECONDS      ( "milliseconds",   "ms",  Units::TYPE_TEMPORAL, 1000.0 );
-OSGEARTH_REGISTER_UNITS( MILLISECONDS, &Units::MILLISECONDS );
-
 const Units Units::MINUTES           ( "minutes",        "min", Units::TYPE_TEMPORAL, 1.0/60.0 );
-OSGEARTH_REGISTER_UNITS( MINUTES, &Units::MINUTES );
-
 const Units Units::SECONDS           ( "seconds",        "s",   Units::TYPE_TEMPORAL, 1.0 );
-OSGEARTH_REGISTER_UNITS( SECONDS, &Units::SECONDS );
-
 const Units Units::WEEKS             ( "weeks",          "wk",  Units::TYPE_TEMPORAL, 1.0/604800.0 );
-OSGEARTH_REGISTER_UNITS( WEEKS, &Units::WEEKS );
-
 
 const Units Units::FEET_PER_SECOND      ( "feet per second",         "ft/s", Units::FEET,           Units::SECONDS );
-OSGEARTH_REGISTER_UNITS( FEET_PER_SECOND, &Units::FEET_PER_SECOND );
-
 const Units Units::YARDS_PER_SECOND     ( "yards per second",        "yd/s", Units::YARDS,          Units::SECONDS );
-OSGEARTH_REGISTER_UNITS( YARDS_PER_SECOND, &Units::YARDS_PER_SECOND );
-
 const Units Units::METERS_PER_SECOND    ( "meters per second",       "m/s",  Units::METERS,         Units::SECONDS );
-OSGEARTH_REGISTER_UNITS( METERS_PER_SECOND, &Units::METERS_PER_SECOND );
-
 const Units Units::KILOMETERS_PER_SECOND( "kilometers per second",   "km/s", Units::KILOMETERS,     Units::SECONDS );
-OSGEARTH_REGISTER_UNITS( KILOMETERS_PER_SECOND, &Units::KILOMETERS_PER_SECOND );
-
 const Units Units::KILOMETERS_PER_HOUR  ( "kilometers per hour",     "kmh",  Units::KILOMETERS,     Units::SECONDS );
-OSGEARTH_REGISTER_UNITS( KILOMETERS_PER_HOUR, &Units::KILOMETERS_PER_HOUR );
-
 const Units Units::MILES_PER_HOUR       ( "miles per hour",          "mph",  Units::MILES,          Units::HOURS );
-OSGEARTH_REGISTER_UNITS( MILES_PER_HOUR, &Units::MILES_PER_HOUR );
-
 const Units Units::DATA_MILES_PER_HOUR  ( "data miles per hour",     "dm/h", Units::DATA_MILES,     Units::HOURS );
-OSGEARTH_REGISTER_UNITS( DATA_MILES_PER_HOUR, &Units::DATA_MILES_PER_HOUR );
-
 const Units Units::KNOTS                ( "nautical miles per hour", "kts",  Units::NAUTICAL_MILES, Units::HOURS );
-OSGEARTH_REGISTER_UNITS( KNOTS, &Units::KNOTS );
 
+const Units Units::PIXELS               ( "pixels", "px", Units::TYPE_SCREEN_SIZE, 1.0 );
diff --git a/src/osgEarth/Utils b/src/osgEarth/Utils
index e3c0081..5f1d52c 100644
--- a/src/osgEarth/Utils
+++ b/src/osgEarth/Utils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Utils.cpp b/src/osgEarth/Utils.cpp
index 6f87a56..c02d3c0 100644
--- a/src/osgEarth/Utils.cpp
+++ b/src/osgEarth/Utils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -63,6 +63,8 @@ osg::AutoTransform()
 {
     // deactivate culling for the first traversal. We will reactivate it later.
     setCullingActive( false );
+    setMinimumScale ( 1.0 );
+    setMinPixelWidthAtScaleOne( 10 );
 }
 
 void
diff --git a/src/osgEarth/Version b/src/osgEarth/Version
index b578931..b8ab10d 100644
--- a/src/osgEarth/Version
+++ b/src/osgEarth/Version
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -25,10 +25,10 @@
 extern "C" {
 
 #define OSGEARTH_MAJOR_VERSION    2
-#define OSGEARTH_MINOR_VERSION    3
+#define OSGEARTH_MINOR_VERSION    4
 #define OSGEARTH_PATCH_VERSION    0
 #define OSGEARTH_SOVERSION        0
-#define OSGEARTH_RC_VERSION       0
+#define OSGEARTH_RC_VERSION       2
 
 /* 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 344d773..c601f36 100644
--- a/src/osgEarth/Version.cpp
+++ b/src/osgEarth/Version.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/VerticalDatum b/src/osgEarth/VerticalDatum
index 512a77f..19ecb22 100644
--- a/src/osgEarth/VerticalDatum
+++ b/src/osgEarth/VerticalDatum
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/VerticalDatum.cpp b/src/osgEarth/VerticalDatum.cpp
index 2fd567f..2519dfb 100644
--- a/src/osgEarth/VerticalDatum.cpp
+++ b/src/osgEarth/VerticalDatum.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Viewpoint b/src/osgEarth/Viewpoint
index 2ff2788..3bd9b1c 100644
--- a/src/osgEarth/Viewpoint
+++ b/src/osgEarth/Viewpoint
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/Viewpoint.cpp b/src/osgEarth/Viewpoint.cpp
index 6cbd37e..2eca790 100644
--- a/src/osgEarth/Viewpoint.cpp
+++ b/src/osgEarth/Viewpoint.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/VirtualProgram b/src/osgEarth/VirtualProgram
index 5561189..43b8fdd 100644
--- a/src/osgEarth/VirtualProgram
+++ b/src/osgEarth/VirtualProgram
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -30,9 +30,11 @@
 #include <map>
 
 #ifdef OSG_GLES2_AVAILABLE
-    #define GLSL_VERSION_STR "100"
+#    define GLSL_VERSION_STR             "100"
+#    define GLSL_DEFAULT_PRECISION_FLOAT "precision mediump float;"
 #else
-    #define GLSL_VERSION_STR "110" 
+#    define GLSL_VERSION_STR             "110" 
+#    define GLSL_DEFAULT_PRECISION_FLOAT ""
 #endif
 
 namespace osgEarth
@@ -42,12 +44,21 @@ namespace osgEarth
         // User function injection points.
         enum FunctionLocation
         {
-            LOCATION_VERTEX_PRE_COLORING    =0,   LOCATION_VERTEX_PRE_TEXTURING =0, // alias
-            LOCATION_VERTEX_PRE_LIGHTING    =1,
-            LOCATION_VERTEX_POST_LIGHTING   =2,
-            LOCATION_FRAGMENT_PRE_COLORING  =3,   LOCATION_FRAGMENT_PRE_TEXTURING =3, // alias
-            LOCATION_FRAGMENT_PRE_LIGHTING  =4,
-            LOCATION_FRAGMENT_POST_LIGHTING =5
+            // vertex is in model space (equivalent to gl_Vertex).
+            LOCATION_VERTEX_MODEL = 0,
+
+            // vertex is in view(aka eye) coordinates, with the camera at 0,0,0 
+            // looking down the -Z axis.
+            LOCATION_VERTEX_VIEW = 1,
+
+            // vertex is in post-perspective coordinates; [-w..w] along each axis
+            LOCATION_VERTEX_CLIP = 2,
+
+            // fragment is being colored.
+            LOCATION_FRAGMENT_COLORING = 3,
+
+            // fragment is being lit.
+            LOCATION_FRAGMENT_LIGHTING = 4
         };
 
         // set of user functions, ordered by priority.
@@ -94,34 +105,6 @@ namespace osgEarth
             float                        priority =1.0f );
 
         /**
-         * Installs default shaders for implementing basic coloring and lighting.
-         * The default shaders come from the ShaderFactory.
-         */
-        void installDefaultColoringAndLightingShaders(
-            unsigned                           numTextures =0u,
-            osg::StateAttribute::OverrideValue qualifiers  =osg::StateAttribute::ON );
-
-        /**
-         * Installs default shader for basic coloring/texturing.
-         */
-        void installDefaultColoringShaders( 
-            unsigned                           numTextures =0u,
-            osg::StateAttribute::OverrideValue qualifiers  =osg::StateAttribute::ON );
-
-
-        /**
-         * Installs default shader for basic lighting.
-         */
-        void installDefaultLightingShaders(
-            osg::StateAttribute::OverrideValue qualifiers  =osg::StateAttribute::ON );
-
-        /**
-         * Sets whether to use lighting shaders at all - set this to false if you
-         * don't want lighting shaders (normal or inherited) included in the program.
-         */
-        void setUseLightingShaders(bool value);
-
-        /**
          * Whether this VP should inherit shaders from parent state sets. This is
          * the normal operation. You can set this to "false" to "reset" the VP.
          */
@@ -207,8 +190,8 @@ namespace osgEarth
         unsigned int                 _mask;
         AttribBindingList            _attribBindingList;
         bool                         _useLightingShaders;
-        osg::ref_ptr<osg::Shader>    _vertMain;
-        osg::ref_ptr<osg::Shader>    _fragMain;
+        //osg::ref_ptr<osg::Shader>    _vertMain;
+        //osg::ref_ptr<osg::Shader>    _fragMain;
 
         ShaderComp::FunctionLocationMap _functions;
         ShaderComp::FunctionLocationMap _accumulatedFunctions;
diff --git a/src/osgEarth/VirtualProgram.cpp b/src/osgEarth/VirtualProgram.cpp
index ad549fc..30354f7 100644
--- a/src/osgEarth/VirtualProgram.cpp
+++ b/src/osgEarth/VirtualProgram.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -38,9 +38,6 @@ using namespace osgEarth::ShaderComp;
 
 //------------------------------------------------------------------------
 
-#define VERTEX_MAIN             "osgearth_vert_main"
-#define FRAGMENT_MAIN           "osgearth_frag_main"
-
 // environment variable control
 #define OSGEARTH_DUMP_SHADERS  "OSGEARTH_DUMP_SHADERS"
 #define OSGEARTH_MERGE_SHADERS "OSGEARTH_MERGE_SHADERS"
@@ -88,10 +85,6 @@ _mask              ( mask ),
 _inherit           ( true ),
 _useLightingShaders( true )
 {
-    // because we sometimes update/change the attribute's members from within the apply() method
-    // 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 )
     {
@@ -194,6 +187,10 @@ VirtualProgram::setShader(const std::string&                 shaderID,
     if ( !shader || shader->getType() ==  osg::Shader::UNDEFINED ) 
         return NULL;
 
+    // pre-processes the shader's source to include GLES uniforms as necessary
+    // (no-op on non-GLES)
+    ShaderPreProcessor::run( shader );
+
     shader->setName( shaderID );
     _shaderMap[shaderID] = ShaderEntry(shader, ov);
 
@@ -252,7 +249,7 @@ VirtualProgram::setFunction(const std::string& functionName,
     
     ofm.insert( std::pair<float,std::string>( priority, functionName ) );
 
-    osg::Shader::Type type = (int)location <= (int)LOCATION_VERTEX_POST_LIGHTING ?
+    osg::Shader::Type type = (int)location <= (int)LOCATION_VERTEX_CLIP ?
         osg::Shader::VERTEX : osg::Shader::FRAGMENT;
 
     setShader( functionName, new osg::Shader(type, shaderSource) );
@@ -271,6 +268,13 @@ VirtualProgram::removeShader( const std::string& shaderID )
             if ( j->second == shaderID )
             {
                 ofm.erase( j );
+
+                // if the function map for this location is now empty,
+                // remove the location map altogether.
+                if ( ofm.size() == 0 )
+                {
+                    _functions.erase( i );
+                }
                 break;
             }
         }
@@ -313,62 +317,12 @@ VirtualProgram::addToAccumulatedMap(ShaderMap&         accumShaderMap,
 
 
 void
-VirtualProgram::installDefaultColoringAndLightingShaders(unsigned                           numTextures,
-                                                         osg::StateAttribute::OverrideValue qual )
-{
-    ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
-
-    this->setShader( sf->createDefaultColoringVertexShader(numTextures), qual );
-    this->setShader( sf->createDefaultLightingVertexShader(), qual );
-
-    this->setShader( sf->createDefaultColoringFragmentShader(numTextures), qual );
-    this->setShader( sf->createDefaultLightingFragmentShader(), qual );
-
-    setUseLightingShaders( true );
-}
-
-
-void
-VirtualProgram::installDefaultLightingShaders(osg::StateAttribute::OverrideValue qual)
-{
-    ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
-
-    this->setShader( sf->createDefaultLightingVertexShader(), qual );
-    this->setShader( sf->createDefaultLightingFragmentShader(), qual );
-
-    setUseLightingShaders( true );
-}
-
-
-void
-VirtualProgram::installDefaultColoringShaders(unsigned                           numTextures,
-                                              osg::StateAttribute::OverrideValue qual )
-{
-    ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
-
-    this->setShader( sf->createDefaultColoringVertexShader(numTextures), qual );
-    this->setShader( sf->createDefaultColoringFragmentShader(numTextures), qual );
-}
-
-
-void
 VirtualProgram::setInheritShaders( bool value )
 {
     if ( _inherit != value )
     {
         _inherit = value;
-        _programCache.clear();
-        _accumulatedFunctions.clear();
-    }
-}
-
-
-void
-VirtualProgram::setUseLightingShaders( bool value )
-{
-    if ( _useLightingShaders != value )
-    {
-        _useLightingShaders = value;
+        // not particularly thread safe if called after use.. meh
         _programCache.clear();
         _accumulatedFunctions.clear();
     }
@@ -531,40 +485,38 @@ VirtualProgram::buildProgram(osg::State&        state,
     // build a new set of accumulated functions, to support the creation of main()
     refreshAccumulatedFunctions( state );
 
-    // No matching program in the cache; make it.
-    ShaderFactory* sf = osgEarth::Registry::instance()->getShaderFactory();
+    // create new MAINs for this function stack.
+    osg::Shader* vertMain = Registry::shaderFactory()->createVertexShaderMain( _accumulatedFunctions );
+    osg::Shader* fragMain = Registry::shaderFactory()->createFragmentShaderMain( _accumulatedFunctions );
 
-    // create the MAINs. Save the old ones so we can replace them in the cache.
-    osg::ref_ptr<osg::Shader> oldVertMain = _vertMain.get();
-    _vertMain = sf->createVertexShaderMain( _accumulatedFunctions, _useLightingShaders );
-
-    osg::ref_ptr<osg::Shader> oldFragMain = _fragMain.get();
-    _fragMain = sf->createFragmentShaderMain( _accumulatedFunctions, _useLightingShaders );
-
-    // rebuild the shader list now that we've changed the shader map.
+    // build a new "key vector" now that we've changed the shader map.
+    // we call is a key vector because it uniquely identifies this shader program
+    // based on its accumlated function set.
     ShaderVector keyVector;
     for( ShaderMap::iterator i = accumShaderMap.begin(); i != accumShaderMap.end(); ++i )
     {
         keyVector.push_back( i->second.first.get() );
     }
 
-    // finally, add the mains (AFTER building the key vector)
+    // finally, add the mains (AFTER building the key vector .. we don't want or
+    // need to mains in the key vector since they are completely derived from the
+    // other elements of the key vector.)
     ShaderVector buildVector( keyVector );
-    buildVector.push_back( _vertMain.get() );
-    buildVector.push_back( _fragMain.get() );
-
+    buildVector.push_back( vertMain );
+    buildVector.push_back( fragMain );
 
-    // Create a new program and add all our shaders.
     if ( s_dumpShaders )
         OE_NOTICE << LC << "---------PROGRAM: " << getName() << " ---------------\n" << std::endl;
 
+    // Create the new program.
     osg::Program* program = new osg::Program();
     program->setName(getName());
     addShadersToProgram( buildVector, accumAttribBindings, program );
-    //addShadersToProgram( vec, accumAttribBindings, program );
     addTemplateDataToProgram( program );
 
 
+#if 0 // gw - obe, don't do this anymore
+
     // 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 ( oldVertMain.valid() || oldFragMain.valid() )
@@ -590,6 +542,7 @@ VirtualProgram::buildProgram(osg::State&        state,
 
         _programCache = newProgramCache;
     }
+#endif
 
     // finally, put own new program in the cache.
     _programCache[ keyVector ] = program;
diff --git a/src/osgEarth/XmlUtils b/src/osgEarth/XmlUtils
index 5508831..d05377e 100644
--- a/src/osgEarth/XmlUtils
+++ b/src/osgEarth/XmlUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/XmlUtils.cpp b/src/osgEarth/XmlUtils.cpp
index 77c9cc1..74d557d 100644
--- a/src/osgEarth/XmlUtils.cpp
+++ b/src/osgEarth/XmlUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarth/optional b/src/osgEarth/optional
index 3260bbd..c93e070 100644
--- a/src/osgEarth/optional
+++ b/src/osgEarth/optional
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -46,12 +46,13 @@ namespace osgEarth
         bool isSet() const { return _set; }
         void unset() { _set = false; _value=_defaultValue; }
 
-        //T& get() { return _value; }
         const T& get() const { return _value; }
         const T& value() const { return _value; }
         const T& defaultValue() const { return _defaultValue; }
         T temp_copy() const { return _value; }
 
+        const T& getOrUse(const T& fallback) const { return _set ? _value : fallback; }
+
         // gets a mutable reference, automatically setting
         T& mutable_value() { _set = true; return _value; }
 
diff --git a/src/osgEarthAnnotation/AnnotationData b/src/osgEarthAnnotation/AnnotationData
index 6e9e9d2..5016e21 100644
--- a/src/osgEarthAnnotation/AnnotationData
+++ b/src/osgEarthAnnotation/AnnotationData
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationData.cpp b/src/osgEarthAnnotation/AnnotationData.cpp
index 932d0d9..72f7532 100644
--- a/src/osgEarthAnnotation/AnnotationData.cpp
+++ b/src/osgEarthAnnotation/AnnotationData.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationEditing b/src/osgEarthAnnotation/AnnotationEditing
index 5a172cb..96cab29 100644
--- a/src/osgEarthAnnotation/AnnotationEditing
+++ b/src/osgEarthAnnotation/AnnotationEditing
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationEditing.cpp b/src/osgEarthAnnotation/AnnotationEditing.cpp
index a965285..64f0dd2 100644
--- a/src/osgEarthAnnotation/AnnotationEditing.cpp
+++ b/src/osgEarthAnnotation/AnnotationEditing.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -181,6 +181,8 @@ CircleNodeEditor::updateDraggers()
             osg::RadiansToDegrees(lon),
             osg::RadiansToDegrees(lat));
 
+        draggerLocation.z() = osg::maximum(draggerLocation.z(), getPositionDragger()->getPosition().z());
+
         _radiusDragger->setPosition( draggerLocation, false );
     }
 }
@@ -282,10 +284,14 @@ EllipseNodeEditor::updateDraggers()
         double lat, lon;
         GeoMath::destination(osg::DegreesToRadians( location.y() ), osg::DegreesToRadians( location.x() ), osg::PI_2 - rotation, minorR, lat, lon, em->getRadiusEquator());        
 
-        _minorDragger->setPosition( GeoPoint(location.getSRS(), osg::RadiansToDegrees( lon ), osg::RadiansToDegrees( lat )), false);
+        GeoPoint minorLocation(location.getSRS(), osg::RadiansToDegrees( lon ), osg::RadiansToDegrees( lat ));
+        minorLocation.z() = osg::maximum(minorLocation.z(), getPositionDragger()->getPosition().z());
+        _minorDragger->setPosition( minorLocation, false);
 
         GeoMath::destination(osg::DegreesToRadians( location.y() ), osg::DegreesToRadians( location.x() ), -rotation, majorR, lat, lon, em->getRadiusEquator());                
-        _majorDragger->setPosition( GeoPoint(location.getSRS(), osg::RadiansToDegrees( lon ), osg::RadiansToDegrees( lat )), false);
+        GeoPoint majorLocation(location.getSRS(), osg::RadiansToDegrees( lon ), osg::RadiansToDegrees( lat ));
+        majorLocation.z() = osg::maximum(majorLocation.z(), getPositionDragger()->getPosition().z());
+        _majorDragger->setPosition( majorLocation, false);
     }
 }
 
diff --git a/src/osgEarthAnnotation/AnnotationNode b/src/osgEarthAnnotation/AnnotationNode
index eee588b..71bc7af 100644
--- a/src/osgEarthAnnotation/AnnotationNode
+++ b/src/osgEarthAnnotation/AnnotationNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -28,8 +28,16 @@
 #include <osgEarth/TileKey>
 #include <osg/Switch>
 
-#define META_AnnotationNode(library,type) \
-    META_Node(library,type); \
+
+#define META_AnnotationNode(library,name) \
+    META_Node(library,name) \
+    virtual bool accept(Decoration* ds, bool enable) { return ds->apply(*this, enable); }
+
+#define META_AnnotationNodeAbstract(library,name) \
+    virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const name *>(obj)!=NULL; } \
+    virtual const char* className() const { return #name; } \
+    virtual const char* libraryName() const { return #library; } \
+    virtual void accept(osg::NodeVisitor& nv) { if (nv.validNodeMask(*this)) { nv.pushOntoNodePath(this); nv.apply(*this); nv.popFromNodePath(); } } \
     virtual bool accept(Decoration* ds, bool enable) { return ds->apply(*this, enable); }
 
 // forward decs
@@ -116,6 +124,13 @@ namespace osgEarth { namespace Annotation
         const MapNode* getMapNode() const { return _mapNode.get(); }
 
 
+    public: // Style functions
+
+        virtual void setStyle(const Style& style) { }
+
+        virtual const Style& getStyle() const { return s_emptyStyle; }
+
+
     protected:
 
         osg::ref_ptr<AnnotationData> _annoData;
@@ -132,17 +147,25 @@ namespace osgEarth { namespace Annotation
 
         bool supportsAutoClamping( const Style& style ) const;
         
-        void configureForAltitudeMode( const AltitudeMode& mode );
+        /**
+         * Check the altitude symbology (if present) and configure the
+         * automatic mesh clamping if necessary.
+         */
+        virtual void configureForAltitudeMode( const AltitudeMode& mode );
 
-        // Apply general style information
+        /**
+         * Apply general style information to the node
+         */
         virtual void applyStyle( const Style&);
+        virtual void applyGeneralSymbology(const Style&);
         
         /**
-         * Sets the node to automatically re-clamp to the terrain (if applicable).
+         * Sets the node to automatically re-clamp its geometry to incoming terrain tiles
+         * as they page in (if applicable).
          * Note: you usually don't need to call this directly; it is automatically set
          * based on the symbology. But you can call it to override the automatic setting.
          */
-        virtual void setAutoClamp( bool value );
+        virtual void setCPUAutoClamping( bool value );
 
         /**
          * Whether to activate depth adjustment.
@@ -173,6 +196,7 @@ namespace osgEarth { namespace Annotation
     private:
             
         osg::observer_ptr<MapNode>   _mapNode;
+        static Style s_emptyStyle;
 
 
     public: // internal methods; do not call directly
diff --git a/src/osgEarthAnnotation/AnnotationNode.cpp b/src/osgEarthAnnotation/AnnotationNode.cpp
index 6207e82..9163d7e 100644
--- a/src/osgEarthAnnotation/AnnotationNode.cpp
+++ b/src/osgEarthAnnotation/AnnotationNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -51,6 +51,10 @@ namespace osgEarth { namespace Annotation
 
 //-------------------------------------------------------------------
 
+Style AnnotationNode::s_emptyStyle;
+
+//-------------------------------------------------------------------
+
 AnnotationNode::AnnotationNode(MapNode* mapNode) :
 _mapNode    ( mapNode ),
 _dynamic    ( false ),
@@ -58,7 +62,6 @@ _autoclamp  ( false ),
 _depthAdj   ( false ),
 _activeDs   ( 0L )
 {
-    //nop
     //Note: Cannot call setMapNode() here because it's a virtual function.
     //      Each subclass will be separately responsible at ctor time.
 
@@ -90,6 +93,12 @@ _activeDs   ( 0L )
         bool blending = conf.value<bool>("blending", false);
         getOrCreateStateSet()->setMode( GL_BLEND, (blending?1:0) | osg::StateAttribute::OVERRIDE );
     }
+    else
+    {
+        // blend by default.
+        this->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
+    }
+
 }
 
 AnnotationNode::~AnnotationNode()
@@ -144,7 +153,7 @@ AnnotationNode::setDynamic( bool value )
 }
 
 void
-AnnotationNode::setAutoClamp( bool value )
+AnnotationNode::setCPUAutoClamping( bool value )
 {
     if ( getMapNode() )
     {
@@ -325,6 +334,7 @@ AnnotationNode::clearDecoration()
     {
         this->accept(_activeDs, false);
         _activeDs = 0L;
+        _activeDsName = "";
     }
 }
 
@@ -356,7 +366,7 @@ AnnotationNode::supportsAutoClamping( const Style& style ) const
 void
 AnnotationNode::configureForAltitudeMode( const AltitudeMode& mode )
 {
-    setAutoClamp(
+    setCPUAutoClamping(
         mode == ALTMODE_RELATIVE ||
         (_altitude.valid() && _altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN) );
 }
@@ -367,6 +377,29 @@ AnnotationNode::applyStyle( const Style& style)
     if ( supportsAutoClamping(style) )
     {
         _altitude = style.get<AltitudeSymbol>();
-        setAutoClamp( true );
+        setCPUAutoClamping( true );
+    }
+    applyGeneralSymbology(style);
+}
+
+void
+AnnotationNode::applyGeneralSymbology(const Style& style)
+{
+    const RenderSymbol* render = style.get<RenderSymbol>();
+    if ( render )
+    {
+        if ( render->depthTest().isSet() )
+        {
+            getOrCreateStateSet()->setMode(
+                GL_DEPTH_TEST,
+                (render->depthTest() == true? osg::StateAttribute::ON : osg::StateAttribute::OFF) | osg::StateAttribute::OVERRIDE );
+        }
+
+        if ( render->lighting().isSet() )
+        {
+            getOrCreateStateSet()->setMode(
+                GL_LIGHTING,
+                (render->lighting() == true? osg::StateAttribute::ON : osg::StateAttribute::OFF) | osg::StateAttribute::OVERRIDE );
+        }
     }
 }
diff --git a/src/osgEarthAnnotation/AnnotationRegistry b/src/osgEarthAnnotation/AnnotationRegistry
index 0c491d3..10b34ee 100644
--- a/src/osgEarthAnnotation/AnnotationRegistry
+++ b/src/osgEarthAnnotation/AnnotationRegistry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationRegistry.cpp b/src/osgEarthAnnotation/AnnotationRegistry.cpp
index d49030e..d83d8e0 100644
--- a/src/osgEarthAnnotation/AnnotationRegistry.cpp
+++ b/src/osgEarthAnnotation/AnnotationRegistry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationSettings b/src/osgEarthAnnotation/AnnotationSettings
index 96f18de..55a0151 100644
--- a/src/osgEarthAnnotation/AnnotationSettings
+++ b/src/osgEarthAnnotation/AnnotationSettings
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationSettings.cpp b/src/osgEarthAnnotation/AnnotationSettings.cpp
index dcbfbb4..cf6b156 100644
--- a/src/osgEarthAnnotation/AnnotationSettings.cpp
+++ b/src/osgEarthAnnotation/AnnotationSettings.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/AnnotationUtils b/src/osgEarthAnnotation/AnnotationUtils
index 6bb3b29..c4a2798 100644
--- a/src/osgEarthAnnotation/AnnotationUtils
+++ b/src/osgEarthAnnotation/AnnotationUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -74,16 +74,6 @@ 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,
-            osg::StateAttribute::OverrideValue value    =osg::StateAttribute::ON );
-#endif
-
         /**
          * Builds a graph on top of the specified that that implements a 2-pass
          * rendering scheme for self-occluding or self-intersecting geometies that
@@ -141,6 +131,23 @@ namespace osgEarth { namespace Annotation
          */
         static osg::Node* createEllipsoid( float xr, float yr, float zr, const osg::Vec4& color, float maxAngle =15.0f );
 
+        static osg::Node* createEllipsoid( 
+            float xr, float yr, const osg::Vec4& color, float maxAngle =15.0f,
+            float minLat =-90.0, float maxLat=90.0, float minLon=-180.0, float maxLon=180.0);
+
+        static osg::Geometry* createEllipsoidGeometry( 
+            float major, float minor, const osg::Vec4& color, float maxAngle =15.0f,
+            float minLat =-90.0, float maxLat=90.0, float minLon=-180.0, float maxLon=180.0);
+
+        struct AltitudePolicy
+        {
+            AltitudePolicy() : draping(false), sceneClamping(false), gpuClamping(false) { }
+            bool draping;
+            bool sceneClamping;
+            bool gpuClamping;
+        };
+        static void getAltitudePolicy( const Style& style, AltitudePolicy& b );
+
 
     private:
         AnnotationUtils() { }
diff --git a/src/osgEarthAnnotation/AnnotationUtils.cpp b/src/osgEarthAnnotation/AnnotationUtils.cpp
old mode 100644
new mode 100755
index 2d4052d..df7c198
--- a/src/osgEarthAnnotation/AnnotationUtils.cpp
+++ b/src/osgEarthAnnotation/AnnotationUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -138,9 +138,15 @@ AnnotationUtils::createTextDrawable(const std::string& text,
     }
 
     // this disables the default rendering bin set by osgText::Font. Necessary if we're
-    // going to do decluttering at a higher level
-    osg::StateSet* stateSet = new osg::StateSet();
-    t->setStateSet( stateSet );
+    // going to do decluttering.
+    // TODO: verify that it's still OK to share the font stateset (think so) or does it
+    // need to be marked DYNAMIC
+    if ( t->getStateSet() )
+      t->getStateSet()->setRenderBinToInherit();
+    //osg::StateSet* stateSet = new osg::StateSet();
+    //t->setStateSet( stateSet );
+
+#if 0 // OBE: the decluttering bin is now set higher up (in OrthoNode)
     //osg::StateSet* stateSet = t->getOrCreateStateSet();
 
     if ( symbol && symbol->declutter().isSet() )
@@ -151,12 +157,15 @@ AnnotationUtils::createTextDrawable(const std::string& text,
     {
         stateSet->setRenderBinToInherit();
     }
+#endif
 
+#if 0 // OBE: shadergenerator now takes care of all this
     // add the static "isText=true" uniform; this is a hint for the annotation shaders
     // if they get installed.
-    static osg::ref_ptr<osg::Uniform> s_isTextUniform = new osg::Uniform(osg::Uniform::BOOL, UNIFORM_IS_TEXT());
-    s_isTextUniform->set( true );
-    stateSet->addUniform( s_isTextUniform.get() );
+    //static osg::ref_ptr<osg::Uniform> s_isTextUniform = new osg::Uniform(osg::Uniform::BOOL, UNIFORM_IS_TEXT());
+    //s_isTextUniform->set( true );
+    //stateSet->addUniform( s_isTextUniform.get() );
+#endif
 
     return t;
 }
@@ -171,7 +180,7 @@ AnnotationUtils::createImageGeometry(osg::Image*       image,
         return 0L;
 
     osg::Texture2D* texture = new osg::Texture2D();
-    texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
+    texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR_MIPMAP_LINEAR);
     texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
     texture->setResizeNonPowerOfTwoHint(false);
     texture->setImage( image );
@@ -225,11 +234,13 @@ AnnotationUtils::createImageGeometry(osg::Image*       image,
 
     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
 
+#if 0
     // add the static "isText=true" uniform; this is a hint for the annotation shaders
     // if they get installed.
     static osg::ref_ptr<osg::Uniform> s_isNotTextUniform = new osg::Uniform(osg::Uniform::BOOL, UNIFORM_IS_TEXT());
     s_isNotTextUniform->set( false );
     dstate->addUniform( s_isNotTextUniform.get() );
+#endif
 
     return geom;
 }
@@ -250,86 +261,6 @@ AnnotationUtils::createHighlightUniform()
     return u;
 }
 
-#if 0
-void
-AnnotationUtils::installAnnotationProgram(osg::StateSet*                     stateSet,
-                                          osg::StateAttribute::OverrideValue qualifier)
-{
-    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;
-    static osg::ref_ptr<osg::Uniform>   s_defaultIsTextUniform;
-
-    if ( !s_program.valid() )
-    {
-        Threading::ScopedMutexLock lock(s_mutex);
-        if ( !s_program.valid() )
-        {
-            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"
-                "varying vec4 oeAnno_texCoord; \n"
-                "void oeAnno_vertColoring() \n"
-                "{ \n"
-                "    osg_FrontColor = gl_Color; \n"
-                "    oeAnno_texCoord = gl_MultiTexCoord0; \n"
-                "} \n";
-
-            std::string fragSource = Stringify() <<
-                "#version " << GLSL_VERSION_STR << "\n"
-#ifdef OSG_GLES2_AVAILABLE
-                "precision mediump float;\n"
-#endif
-                "uniform float " << UNIFORM_FADE()      << "; \n"
-                "uniform bool  " << UNIFORM_IS_TEXT()   << "; \n"
-                "uniform sampler2D oeAnno_tex0; \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(color.rgb, color.a * alpha * " << UNIFORM_FADE() << "); \n"
-                "    } \n"
-                "    else \n"
-                "    { \n"
-                "        color = color * texture2D(oeAnno_tex0, oeAnno_texCoord.st) * vec4(1,1,1," << UNIFORM_FADE() << "); \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, 2.0f );
-
-            s_samplerUniform = new osg::Uniform(osg::Uniform::SAMPLER_2D, "oeAnno_tex0");
-            s_samplerUniform->set( 0 );
-
-            s_defaultFadeUniform = createFadeUniform();
-
-            s_defaultIsTextUniform = new osg::Uniform(osg::Uniform::BOOL, "oeAnno_isText");
-            s_defaultIsTextUniform->set( false );
-        }
-    }
-
-    stateSet->setAttributeAndModes( s_program.get(), qualifier );
-    stateSet->addUniform( s_samplerUniform.get() );
-    stateSet->addUniform( s_defaultFadeUniform.get() );
-    stateSet->addUniform( s_defaultIsTextUniform.get() );
-}
-#endif
-
 //-------------------------------------------------------------------------
 
 // This is identical to osg::AutoTransform::accept, except that we (a) removed
@@ -558,6 +489,141 @@ AnnotationUtils::createHemisphere( float r, const osg::Vec4& color, float maxAng
     return installTwoPassAlpha( geode );
 }
 
+    // constucts an ellipsoidal mesh that we will use to draw the atmosphere
+osg::Geometry*
+AnnotationUtils::createEllipsoidGeometry(float majorRadius, 
+                                         float minorRadius,
+                                         const osg::Vec4f& color, 
+                                         float maxAngle,
+                                         float minLat,
+                                         float maxLat,
+                                         float minLon,
+                                         float maxLon)
+{
+    osg::EllipsoidModel em( majorRadius, minorRadius );
+
+    osg::Geometry* geom = new osg::Geometry();
+    geom->setUseVertexBufferObjects(true);
+
+    float latSpan = maxLat - minLat;
+    float lonSpan = maxLon - minLon;
+    float aspectRatio = lonSpan/latSpan;
+
+    int latSegments = std::max( 6, (int)(latSpan / maxAngle) );
+    int lonSegments = std::max( 3, (int)(latSegments * aspectRatio) );
+    //int lonSegments = 2 * latSegments;
+
+    float segmentSize = latSpan/latSegments; // degrees
+    //double segmentSize = 180.0/(double)latSegments; // degrees
+
+    osg::Vec3Array* verts = new osg::Vec3Array();
+    verts->reserve( latSegments * lonSegments );
+
+    bool genTexCoords = false; // TODO: optional
+    osg::Vec2Array* texCoords = 0;
+    if (genTexCoords)
+    {
+        texCoords = new osg::Vec2Array();
+        texCoords->reserve( latSegments * lonSegments );
+        geom->setTexCoordArray( 0, texCoords );
+    }
+
+    osg::Vec3Array* normals = 0;
+    {
+        normals = new osg::Vec3Array();
+        normals->reserve( latSegments * lonSegments );
+        geom->setNormalArray( normals );
+        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX );
+    }
+
+    osg::DrawElementsUShort* el = new osg::DrawElementsUShort( GL_TRIANGLES );
+    el->reserve( latSegments * lonSegments * 6 );
+
+    for( int y = 0; y <= latSegments; ++y )
+    {
+        float lat = minLat + segmentSize * (float)y;
+        //double lat = -90.0 + segmentSize * (double)y;
+        for( int x = 0; x < lonSegments; ++x )
+        {
+            float lon = minLon + segmentSize * (float)x;
+            //double lon = -180.0 + segmentSize * (double)x;
+            double gx, gy, gz;
+            em.convertLatLongHeightToXYZ( osg::DegreesToRadians(lat), osg::DegreesToRadians(lon), 0.0, gx, gy, gz );
+            verts->push_back( osg::Vec3(gx, gy, gz) );
+
+            if (genTexCoords)
+            {
+                double s = (lon + 180) / 360.0;
+                double t = (lat + 90.0) / 180.0;
+                texCoords->push_back( osg::Vec2(s, t ) );
+            }
+
+            if (normals)
+            {
+                osg::Vec3 normal( gx, gy, gz);
+                normal.normalize();
+                normals->push_back( normal );
+            }
+
+            if ( y < latSegments )
+            {
+                int x_plus_1 = x < lonSegments-1 ? x+1 : 0;
+                int y_plus_1 = y+1;
+                el->push_back( y*lonSegments + x );
+                el->push_back( y*lonSegments + x_plus_1 );
+                el->push_back( y_plus_1*lonSegments + x );
+                el->push_back( y*lonSegments + x_plus_1 );
+                el->push_back( y_plus_1*lonSegments + x_plus_1 );
+                el->push_back( y_plus_1*lonSegments + x );
+            }
+        }
+    }
+
+    osg::Vec4Array* c = new osg::Vec4Array(1);
+    (*c)[0] = color;
+    geom->setColorArray( c );
+    geom->setColorBinding( osg::Geometry::BIND_OVERALL );
+
+    geom->setVertexArray( verts );
+    geom->addPrimitiveSet( el );
+
+    return geom;
+}
+
+osg::Node* 
+AnnotationUtils::createEllipsoid(float majorRadius, 
+                                 float minorRadius,
+                                 const osg::Vec4f& color, 
+                                 float maxAngle,
+                                 float minLat,
+                                 float maxLat,
+                                 float minLon,
+                                 float maxLon)
+{
+    osg::Geode* geode = new osg::Geode();
+    geode->addDrawable( createEllipsoidGeometry(majorRadius, minorRadius, color, maxAngle, minLat, maxLat, minLon, maxLon) );
+
+    if ( color.a() < 1.0f )
+    {
+        geode->getOrCreateStateSet()->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
+    }
+
+    bool solid = (maxLat-minLat >= 180.0f && maxLon-minLon >= 360.0f);
+
+    if ( solid )
+    {
+        geode->getOrCreateStateSet()->setAttributeAndModes( new osg::CullFace(osg::CullFace::BACK), 1 );
+    }
+
+    else if ( color.a() < 1.0f )
+    {
+        //geode->getOrCreateStateSet()->setAttributeAndModes( new osg::CullFace(), 0 );
+        return installTwoPassAlpha(geode);
+    }
+
+    return geode;
+}
+
 osg::Node* 
 AnnotationUtils::createEllipsoid( float xr, float yr, float zr, const osg::Vec4& color, float maxAngle )
 {
@@ -782,3 +848,39 @@ AnnotationUtils::styleRequiresAlphaBlending( const Style& style )
 
     return false;
 }
+
+
+void
+AnnotationUtils::getAltitudePolicy(const Style& style, AltitudePolicy& out)
+{
+    out.sceneClamping = false;
+    out.gpuClamping   = false;
+    out.draping       = false;
+
+    // conditions where clamping is not yet compatible
+    bool compatible =
+        !style.has<ExtrusionSymbol>();      // backwards-compability
+
+    if ( compatible )
+    {
+        const AltitudeSymbol* alt = style.get<AltitudeSymbol>();
+        if ( alt )
+        {
+            if (alt->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN || 
+                alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN )
+            {
+                out.sceneClamping = alt->technique() == AltitudeSymbol::TECHNIQUE_SCENE;
+                out.gpuClamping   = alt->technique() == AltitudeSymbol::TECHNIQUE_GPU;
+                out.draping       = alt->technique() == AltitudeSymbol::TECHNIQUE_DRAPE;
+
+                // for instance/markers, GPU clamping falls back on SCENE clamping.
+                if (out.gpuClamping &&
+                    (style.has<InstanceSymbol>() || style.has<MarkerSymbol>()))
+                {
+                    out.gpuClamping   = false;
+                    out.sceneClamping = true;
+                }
+            }
+        }
+    }
+}
diff --git a/src/osgEarthAnnotation/CircleNode b/src/osgEarthAnnotation/CircleNode
index 733cf20..020105e 100644
--- a/src/osgEarthAnnotation/CircleNode
+++ b/src/osgEarthAnnotation/CircleNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -45,16 +45,18 @@ namespace osgEarth { namespace Annotation
          * @param position    Location of the annotation, in map coordinates
          * @param radius      Circle radius
          * @param style       Style defining how the annotation will look
-         * @param draped      Whether to "drape" the annotation down on to the terrain
-         * @param numSegments Hint as to the tessellation of the annotation
+         * @param arcStart    Optional start of arc (default to 0.0 degrees)
+         * @param arcStop     Optional end of arc (default to 360.0 degrees)
+         * @param pie         Optionally make pie shape instead of arc
          */
         CircleNode(
             MapNode*          mapNode,
             const GeoPoint&   position,
-            const Linear&     radius,
+            const Distance&   radius,
             const Style&      style,
-            bool              draped      =true,
-            unsigned          numSegments =0 );
+            const Angle&      arcStart = Angle(0.0, Units::DEGREES),
+            const Angle&      arcEnd = Angle(360.0, Units::DEGREES),
+            const bool        pie = false);
 
         /**
          * Constructs a circle node from its serialized form
@@ -71,12 +73,12 @@ namespace osgEarth { namespace Annotation
         /**
          * Gets the radius
          */
-        const Linear& getRadius() const;
+        const Distance& getRadius() const;
 
         /*
          * Sets the radius of the circle
          */
-        void setRadius(const Linear& radius);
+        void setRadius(const Distance& radius);
 
         /**
          * Gets the number of segments of this circle
@@ -98,6 +100,41 @@ namespace osgEarth { namespace Annotation
          */
         void setStyle( const Style& style );
 
+        /**
+         * Gets the start degrees of this (arc) circle
+         */
+        const Angle& getArcStart(void) const;
+
+        /**
+         * Sets the start degrees of this (arc) circle
+         */
+        void setArcStart(const Angle& arcStart);
+
+        /**
+         * Gets the end degrees of this (arc) circle
+         */
+        const Angle& getArcEnd(void) const;
+
+        /**
+         * Sets the end degrees of this (arc) circle
+         */
+        void setArcEnd(const Angle& arcEnd);
+
+        /**
+         * Gets the pie flag
+         */
+        const bool& getPie(void) const;
+
+        /**
+         * Sets the pie flag
+         */
+        void setPie(const bool& pie);
+
+    public: // LocalizedNode
+
+        osg::MatrixTransform* getTransform() { return _xform.get(); }
+
+
     public: // AnnotationNode overrides
 
         virtual Config getConfig() const;
@@ -112,7 +149,12 @@ namespace osgEarth { namespace Annotation
         Style           _style;
         bool            _draped;
         unsigned        _numSegments;
-        Linear          _radius;
+        Distance        _radius;
+        Angle          _arcStart;
+        Angle          _arcEnd;
+        bool           _pie;
+
+        osg::ref_ptr<osg::MatrixTransform> _xform;
     };
 
 } } // namespace osgEarth::Annotation
diff --git a/src/osgEarthAnnotation/CircleNode.cpp b/src/osgEarthAnnotation/CircleNode.cpp
index a5a8f18..ad45fb8 100644
--- a/src/osgEarthAnnotation/CircleNode.cpp
+++ b/src/osgEarthAnnotation/CircleNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -36,15 +36,19 @@ CircleNode::CircleNode(MapNode*           mapNode,
                        const GeoPoint&    position,
                        const Linear&      radius,
                        const Style&       style,
-                       bool               draped,
-                       unsigned           numSegments) :
+                       const Angle&       arcStart,
+                       const Angle&       arcEnd,
+                       const bool         pie):
 
-LocalizedNode( mapNode, position, false ),
+LocalizedNode( mapNode, position ),
 _radius      ( radius ),
 _style       ( style ),
-_draped      ( draped ),
-_numSegments ( numSegments )
+_arcStart    ( arcStart ),
+_arcEnd      ( arcEnd ),
+_pie         ( pie ),
+_numSegments ( 0 )
 {
+    _xform = new osg::MatrixTransform();
     rebuild();
 }
 
@@ -94,38 +98,79 @@ CircleNode::setStyle( const Style& style )
     rebuild();
 }
 
+const Angle&
+CircleNode::getArcStart(void) const
+{
+	return (_arcStart);
+}
+
+void
+CircleNode::setArcStart(const Angle& arcStart)
+{
+	_arcStart = arcStart;
+	rebuild();
+}
+
+const Angle&
+CircleNode::getArcEnd(void) const
+{
+	return (_arcEnd);
+}
+
+void
+CircleNode::setArcEnd(const Angle& arcEnd)
+{
+	_arcEnd = arcEnd;
+	rebuild();
+}
+
+const bool&
+CircleNode::getPie(void) const
+{
+	return (_pie);
+}
+
+void
+CircleNode::setPie(const bool& pie)
+{
+    _pie = pie;
+    rebuild();
+}
+
 void
 CircleNode::rebuild()
 {
     std::string currentDecoration = getDecoration();
     clearDecoration();
 
-    //Remove all children from this node
-    //removeChildren( 0, getNumChildren() );
-    if ( getRoot()->getNumParents() == 0 )
-    {
-        this->addChild( getRoot() );
-    }
-
-    //Remove all children from the attach point
-    getChildAttachPoint()->removeChildren( 0, getChildAttachPoint()->getNumChildren() );
+    // Reset this node.
+    osgEarth::clearChildren( this );
+    osgEarth::clearChildren( _xform.get() );
+    this->addChild( _xform.get() );
 
     // construct a local-origin circle.
     GeometryFactory factory;
-    Geometry* geom = factory.createCircle(osg::Vec3d(0,0,0), _radius, _numSegments);
+    Geometry* geom = NULL;
+    if (abs(_arcEnd.as(Units::DEGREES) - _arcStart.as(Units::DEGREES)) >= 360.0)
+    {
+        geom = factory.createCircle(osg::Vec3d(0,0,0), _radius, _numSegments);
+    }
+    else
+    {
+        geom = factory.createArc(osg::Vec3d(0,0,0), _radius, _arcStart, _arcEnd, _numSegments, 0L, _pie);
+    }
     if ( geom )
     {
         GeometryCompiler compiler;
         osg::ref_ptr<Feature> feature = new Feature(geom, 0L); //todo: consider the SRS
         osg::Node* node = compiler.compile( feature.get(), _style, FilterContext(0L) );
         if ( node )
-        {           
-            getChildAttachPoint()->addChild( node );
-            getDrapeable()->setDraped( _draped );
+        {
+            _xform->addChild( node );
+            this->replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) );
         }
 
-        applyStyle( _style );
-
+        applyGeneralSymbology( _style );
         setLightingIfNotSet( false );
     }
 
@@ -143,12 +188,12 @@ CircleNode::CircleNode(MapNode*              mapNode,
                        const osgDB::Options* dbOptions) :
 LocalizedNode( mapNode, conf ),
 _radius      ( 1.0, Units::KILOMETERS ),
-_draped      ( false ),
 _numSegments ( 0 )
 {
+    _xform = new osg::MatrixTransform();
+
     conf.getObjIfSet( "radius", _radius );
     conf.getObjIfSet( "style",  _style );
-    conf.getIfSet   ( "draped", _draped );
     conf.getIfSet   ( "num_segments", _numSegments );
 
     if ( conf.hasChild("position") )
@@ -166,8 +211,6 @@ CircleNode::getConfig() const
 
     if ( _numSegments != 0 )
         conf.add( "num_segments", _numSegments );
-    if ( _draped != false )
-        conf.add( "draped", _draped );
 
     conf.addObj( "position", getPosition() );
 
diff --git a/src/osgEarthAnnotation/Common b/src/osgEarthAnnotation/Common
index de7e079..14aae38 100644
--- a/src/osgEarthAnnotation/Common
+++ b/src/osgEarthAnnotation/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/Decluttering b/src/osgEarthAnnotation/Decluttering
index 54f746e..686c39c 100644
--- a/src/osgEarthAnnotation/Decluttering
+++ b/src/osgEarthAnnotation/Decluttering
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -76,7 +76,8 @@ namespace osgEarth { namespace Annotation
               _minAnimScale         ( 0.45f ),
               _inAnimTime           ( 0.40f ),
               _outAnimTime          ( 0.00f ),
-              _sortByPriority       ( false )
+              _sortByPriority       ( false ),
+              _maxObjects           ( INT_MAX )
         {
             fromConfig(conf);
         }
@@ -103,16 +104,21 @@ namespace osgEarth { namespace Annotation
         optional<bool>& sortByPriority() { return _sortByPriority; }
         const optional<bool>& sortByPriority() const { return _sortByPriority; }
 
+        /** Maximum number of objects to draw after sorting */
+        optional<unsigned>& maxObjects() { return _maxObjects; }
+        const optional<unsigned>& maxObjects() const { return _maxObjects; }
+
     public:
 
         Config getConfig() const;
 
     protected:
-        optional<float> _minAnimAlpha;
-        optional<float> _minAnimScale;
-        optional<float> _inAnimTime;
-        optional<float> _outAnimTime;
-        optional<bool>  _sortByPriority;
+        optional<float>    _minAnimAlpha;
+        optional<float>    _minAnimScale;
+        optional<float>    _inAnimTime;
+        optional<float>    _outAnimTime;
+        optional<bool>     _sortByPriority;
+        optional<unsigned> _maxObjects;
 
         void fromConfig( const Config& conf );
     };
diff --git a/src/osgEarthAnnotation/Decluttering.cpp b/src/osgEarthAnnotation/Decluttering.cpp
index d8e305e..791379d 100644
--- a/src/osgEarthAnnotation/Decluttering.cpp
+++ b/src/osgEarthAnnotation/Decluttering.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -19,8 +19,9 @@
 #include <osgEarthAnnotation/Decluttering>
 #include <osgEarthAnnotation/AnnotationUtils>
 #include <osgEarthAnnotation/AnnotationData>
-#include <osgEarth/Utils>
 #include <osgEarth/ThreadingUtils>
+#include <osgEarth/Utils>
+#include <osgEarth/VirtualProgram>
 #include <osgUtil/RenderBin>
 #include <osgUtil/StateGraph>
 #include <osgText/Text>
@@ -30,6 +31,8 @@
 
 #define LC "[Declutter] "
 
+#define FADE_UNIFORM_NAME "oe_declutter_fade"
+
 using namespace osgEarth;
 using namespace osgEarth::Annotation;
 
@@ -103,6 +106,14 @@ namespace
     };
 
     static bool s_enabledGlobally = true;
+
+    static char* s_faderFS =
+        "#version " GLSL_VERSION_STR "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n"
+        "uniform float " FADE_UNIFORM_NAME ";\n"
+        "void oe_declutter_apply_fade(inout vec4 color) { \n"
+        "    color.a *= " FADE_UNIFORM_NAME ";\n"
+        "}\n";
 }
 
 //----------------------------------------------------------------------------
@@ -115,6 +126,7 @@ DeclutteringOptions::fromConfig( const Config& conf )
     conf.getIfSet( "in_animation_time",   _inAnimTime );
     conf.getIfSet( "out_animation_time",  _outAnimTime );
     conf.getIfSet( "sort_by_priority",    _sortByPriority );
+    conf.getIfSet( "max_objects",         _maxObjects );
 }
 
 Config
@@ -126,6 +138,7 @@ DeclutteringOptions::getConfig() const
     conf.addIfSet( "in_animation_time",   _inAnimTime );
     conf.addIfSet( "out_animation_time",  _outAnimTime );
     conf.addIfSet( "sort_by_priority",    _sortByPriority );
+    conf.addIfSet( "max_objects",         _maxObjects );
     return conf;
 }
 
@@ -184,7 +197,6 @@ struct /*internal*/ DeclutterSort : public osgUtil::RenderBin::SortCallback
             // default behavior:
             bin->copyLeavesFromStateGraphListToRenderLeafList();
             std::sort( leaves.begin(), leaves.end(), SortFrontToBackPreservingGeodeTraversalOrder() );
-            //bin->sortFrontToBack();
         }
 
         // nothing to sort? bail out
@@ -217,9 +229,13 @@ struct /*internal*/ DeclutterSort : public osgUtil::RenderBin::SortCallback
         std::set<const osg::Node*> culledParents;
 
         const DeclutteringOptions& options = _context->_options;
+        unsigned limit = *options.maxObjects();
 
         // Go through each leaf and test for visibility.
-        for( osgUtil::RenderBin::RenderLeafList::iterator i = leaves.begin(); i != leaves.end(); ++i )
+        // Enforce the "max objects" limit along the way.
+        for(osgUtil::RenderBin::RenderLeafList::iterator i = leaves.begin(); 
+            i != leaves.end() && local._passed.size() < limit; 
+            ++i )
         {
             bool visible = true;
 
@@ -291,13 +307,14 @@ struct /*internal*/ DeclutterSort : public osgUtil::RenderBin::SortCallback
             }
 
             // modify the leaf's modelview matrix to correctly position it in the 2D ortho
-            // projection when it's drawn later. (Note: we need a new RefMatrix since the
-            // original might be shared ... potential optimization here)
-            // We'll also preserve the scale.
+            // projection when it's drawn later. We'll also preserve the scale.
             osg::Matrix newModelView;
             newModelView.makeTranslate( box.xMin() + offset.x(), box.yMin() + offset.y(), 0 );
             newModelView.preMultScale( leaf->_modelview->getScale() );
             
+            // Leaf modelview matrixes are shared (by objects in the traversal stack) so we 
+            // cannot just replace it unfortunately. Have to make a new one. Perhaps a nice
+            // allocation pool is in order here
             leaf->_modelview = new osg::RefMatrix( newModelView );
         }
 
@@ -411,7 +428,7 @@ struct DeclutterDraw : public osgUtil::RenderBin::DrawCallback
         : _context( context )
     {
         // create the fade uniform.
-        _fade = AnnotationUtils::createFadeUniform();
+        _fade = new osg::Uniform( osg::Uniform::FLOAT, FADE_UNIFORM_NAME );
         _fade->set( 1.0f );
     }
 
@@ -558,6 +575,15 @@ public:
         _context = new DeclutterContext();
         clearSortingFunctor();
         setDrawCallback( new DeclutterDraw(_context.get()) );
+
+        // needs its own state set for special magic.
+        osg::StateSet* stateSet = new osg::StateSet();
+        this->setStateSet( stateSet );
+
+        // set up a VP to do fading.
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setFunction( "oe_declutter_apply_fade", s_faderFS, ShaderComp::LOCATION_FRAGMENT_COLORING );
+        stateSet->setAttributeAndModes(vp, 1);
     }
 
     void setSortingFunctor( DeclutterSortFunctor* f )
diff --git a/src/osgEarthAnnotation/Decoration b/src/osgEarthAnnotation/Decoration
index 4564d5b..2a28730 100644
--- a/src/osgEarthAnnotation/Decoration
+++ b/src/osgEarthAnnotation/Decoration
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/Decoration.cpp b/src/osgEarthAnnotation/Decoration.cpp
index 21c7711..e485649 100644
--- a/src/osgEarthAnnotation/Decoration.cpp
+++ b/src/osgEarthAnnotation/Decoration.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/EllipseNode b/src/osgEarthAnnotation/EllipseNode
index 41e4ef3..41a91cb 100644
--- a/src/osgEarthAnnotation/EllipseNode
+++ b/src/osgEarthAnnotation/EllipseNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -47,56 +47,57 @@ namespace osgEarth { namespace Annotation
          * @param radiusMinor Length of 1/2 the minor axis of the ellipse
          * @param rotation    Rotation angle of the ellipse
          * @param style       Style defining how the annotation will look
-         * @param draped      Whether to "drape" the annotation down on to the terrain
-         * @param numSegments Hint as to the tessellation of the ellipse
+         * @param arcStart    Optional start of arc (default to 0.0 degrees)
+         * @param arcStop     Optional end of arc (default to 360.0 degrees)
+         * @param pie         Optionally make pie shape instead of arc
          */
         EllipseNode( 
             MapNode*                mapNode,
             const GeoPoint&         position,
-            const Linear&           radiusMajor,
-            const Linear&           radiusMinor,
-            const Angular&          rotationAngle,
+            const Distance&         radiusMajor,
+            const Distance&         radiusMinor,
+            const Angle&            rotationAngle,
             const Style&            style,
-            bool                    draped      =true,
-            unsigned                numSegments =0 );
+            const Angle&            arcStart = Angle(0.0, Units::DEGREES),
+            const Angle&            arcEnd = Angle(360.0, Units::DEGREES),
+            const bool              pie = false);
 
         virtual ~EllipseNode() { }
 
         /**
          * Gets the major radius
          */
-        const Linear& getRadiusMajor() const;
+        const Distance& getRadiusMajor() const;
 
         /**
          * Gets the minor radius
          */
-        const Linear& getRadiusMinor() const;
+        const Distance& getRadiusMinor() const;
 
         /**
          * Sets the major radius
          */
-        void setRadiusMajor( const Linear& radiusMajor );
+        void setRadiusMajor( const Distance& radiusMajor );
 
         /**
          * Sets the minor radius
          */
-        void setRadiusMinor( const Linear& radiusMinor );
+        void setRadiusMinor( const Distance& radiusMinor );
 
         /**
          * Sets the major and minor radii
          */
-        void setRadii( const Linear& radiusMajor, const Linear& radiusMinor );
+        void setRadii( const Distance& radiusMajor, const Distance& radiusMinor );
 
         /*
          * Gets the rotation angle
          */
-        const Angular& getRotationAngle() const;
-
+        const Angle& getRotationAngle() const;
 
         /**
          * Sets the rotation angle
          */
-        void setRotationAngle(const Angular& rotationAngle);
+        void setRotationAngle(const Angle& rotationAngle);
 
         /**
          * Gets the number of segments
@@ -108,7 +109,6 @@ namespace osgEarth { namespace Annotation
          */
         void setNumSegments(unsigned int numSegments );
 
-
         /**
          * Gets the style
          */
@@ -120,6 +120,36 @@ namespace osgEarth { namespace Annotation
         void setStyle( const Style& style );
 
         /**
+         * Gets the start degrees of this (arc) circle
+         */
+        const Angle& getArcStart(void) const;
+
+        /**
+         * Sets the start degrees of this (arc) circle
+         */
+        void setArcStart(const Angle& arcStart);
+
+        /**
+         * Gets the end degrees of this (arc) circle
+         */
+        const Angle& getArcEnd(void) const;
+
+        /**
+         * Sets the end degrees of this (arc) circle
+         */
+        void setArcEnd(const Angle& arcEnd);
+
+        /**
+         * Gets the pie flag
+         */
+        const bool& getPie(void) const;
+
+        /**
+         * Sets the pie flag
+         */
+        void setPie(const bool& pie);
+
+        /**
          * Gets draped property
          */
         bool isDraped() const { return _draped; }
@@ -130,6 +160,11 @@ namespace osgEarth { namespace Annotation
         virtual Config getConfig() const;
 
 
+    public: // LocalizedNode
+
+        osg::MatrixTransform* getTransform() { return _xform.get(); }
+
+
     private:
         EllipseNode() { }
         EllipseNode(const EllipseNode& rhs, const osg::CopyOp& op) { }
@@ -138,10 +173,14 @@ namespace osgEarth { namespace Annotation
         
         Style _style;
         bool _draped;
-        Angular _rotationAngle;
-        Linear _radiusMajor;
-        Linear _radiusMinor;
+        Angle _rotationAngle;
+        Distance _radiusMajor;
+        Distance _radiusMinor;
+        Angle _arcStart;
+        Angle _arcEnd;
+        bool  _pie;
         unsigned int _numSegments;
+        osg::ref_ptr<osg::MatrixTransform> _xform;
     };
 
 } } // namespace osgEarth::Annotation
diff --git a/src/osgEarthAnnotation/EllipseNode.cpp b/src/osgEarthAnnotation/EllipseNode.cpp
index a4ae020..2581187 100644
--- a/src/osgEarthAnnotation/EllipseNode.cpp
+++ b/src/osgEarthAnnotation/EllipseNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -35,16 +35,20 @@ EllipseNode::EllipseNode(MapNode*          mapNode,
                          const Linear&     radiusMinor,
                          const Angular&    rotationAngle,
                          const Style&      style,
-                         bool              draped,
-                         unsigned          numSegments) :
-LocalizedNode( mapNode, position ),
-_radiusMajor( radiusMajor ),
-_radiusMinor( radiusMinor ),
+                         const Angle&      arcStart,
+                         const Angle&      arcEnd,
+                         const bool        pie) :
+LocalizedNode ( mapNode, position ),
+_radiusMajor  ( radiusMajor ),
+_radiusMinor  ( radiusMinor ),
 _rotationAngle( rotationAngle ),
-_style(style ),
-_draped( draped ),
-_numSegments( numSegments )
+_style        ( style ),
+_arcStart     ( arcStart ),
+_arcEnd       ( arcEnd ),
+_pie          ( pie ),
+_numSegments  ( 0 )
 {
+    _xform = new osg::MatrixTransform();
     rebuild();
 }
 
@@ -129,7 +133,44 @@ EllipseNode::setRotationAngle(const Angular& rotationAngle)
         rebuild();
     }
 }
+const Angle&
+EllipseNode::getArcStart(void) const
+{
+    return (_arcStart);
+}
+
+void
+EllipseNode::setArcStart(const Angle& arcStart)
+{
+    _arcStart = arcStart;
+    rebuild();
+}
 
+const Angle&
+EllipseNode::getArcEnd(void) const
+{
+    return (_arcEnd);
+}
+
+void
+EllipseNode::setArcEnd(const Angle& arcEnd)
+{
+    _arcEnd = arcEnd;
+    rebuild();
+}
+
+const bool&
+EllipseNode::getPie(void) const
+{
+	return (_pie);
+}
+
+void
+EllipseNode::setPie(const bool& pie)
+{
+	_pie = pie;
+	rebuild();
+}
 
 void
 EllipseNode::rebuild()
@@ -138,18 +179,22 @@ EllipseNode::rebuild()
     clearDecoration();
 
     //Remove all children from this node
-    //removeChildren( 0, getNumChildren() );
-    if ( getRoot()->getNumParents() == 0 )
-    {
-        this->addChild( getRoot() );
-    }
-
-    //Remove all children from the attach point
-    getChildAttachPoint()->removeChildren( 0, getChildAttachPoint()->getNumChildren() );
+    osgEarth::clearChildren( this );
+    osgEarth::clearChildren( _xform.get() );
+    this->addChild( _xform.get() );
 
     // construct a local-origin ellipse.
     GeometryFactory factory;
-    Geometry* geom = factory.createEllipse(osg::Vec3d(0,0,0), _radiusMajor, _radiusMinor, _rotationAngle, _numSegments);
+    Geometry* geom = NULL;
+
+    if (abs(_arcEnd.as(Units::DEGREES) - _arcStart.as(Units::DEGREES)) >= 360.0)
+    {
+        geom = factory.createEllipse(osg::Vec3d(0,0,0), _radiusMajor, _radiusMinor, _rotationAngle, _numSegments);
+    }
+    else
+    {
+        geom = factory.createEllipticalArc(osg::Vec3d(0,0,0), _radiusMajor, _radiusMinor, _rotationAngle, _arcStart, _arcEnd, _numSegments, 0L, _pie);
+    }
     if ( geom )
     {
         GeometryCompiler compiler;
@@ -157,12 +202,11 @@ EllipseNode::rebuild()
         osg::Node* node = compiler.compile( feature.get(), _style, FilterContext(0L) );
         if ( node )
         {
-            getChildAttachPoint()->addChild( node );
-            getDrapeable()->setDraped( _draped );
+            _xform->addChild( node );
+            this->replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) );
         }
 
-        applyStyle( _style );
-
+        applyGeneralSymbology( _style );
         setLightingIfNotSet( false );
     }
 
@@ -183,11 +227,12 @@ LocalizedNode( mapNode, conf ),
 _draped      ( false ),
 _numSegments ( 0 )
 {
+    _xform = new osg::MatrixTransform();
+
     conf.getObjIfSet( "radius_major", _radiusMajor );
     conf.getObjIfSet( "radius_minor", _radiusMinor );
     conf.getObjIfSet( "rotation", _rotationAngle );
     conf.getObjIfSet( "style",  _style );
-    conf.getIfSet   ( "draped", _draped );
     conf.getIfSet   ( "num_segments", _numSegments );
 
     rebuild();
@@ -207,8 +252,6 @@ EllipseNode::getConfig() const
 
     if ( _numSegments != 0 )
         conf.add( "num_segments", _numSegments );
-    if ( _draped != false )
-        conf.add( "draped", _draped );
 
     conf.addObj( "position", getPosition() );
 
diff --git a/src/osgEarthAnnotation/Export b/src/osgEarthAnnotation/Export
index ce68a69..d793d3c 100644
--- a/src/osgEarthAnnotation/Export
+++ b/src/osgEarthAnnotation/Export
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/FeatureEditing b/src/osgEarthAnnotation/FeatureEditing
index 1c082be..a9fadac 100644
--- a/src/osgEarthAnnotation/FeatureEditing
+++ b/src/osgEarthAnnotation/FeatureEditing
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/FeatureEditing.cpp b/src/osgEarthAnnotation/FeatureEditing.cpp
index 8ce1a91..0909e33 100644
--- a/src/osgEarthAnnotation/FeatureEditing.cpp
+++ b/src/osgEarthAnnotation/FeatureEditing.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/FeatureNode b/src/osgEarthAnnotation/FeatureNode
index 38bbd14..70f5399 100644
--- a/src/osgEarthAnnotation/FeatureNode
+++ b/src/osgEarthAnnotation/FeatureNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -33,14 +33,18 @@ namespace osgEarth { namespace Annotation
     using namespace osgEarth::Symbology;
 
     /**
-     * A node that renders a single feature. Since no feature profile is provided,
+     * Renders a single feature. Since no feature profile is provided,
      * the feature must contain geometry that is in the same SRS as the map.
+     * The feature must also include the Style you wish to use.
      */
     class OSGEARTHANNO_EXPORT FeatureNode : public AnnotationNode
     {
     public:
         META_AnnotationNode(osgEarthAnnotation, FeatureNode);
 
+        /**
+         * Constructs a new Feautre Node.
+         */
         FeatureNode( 
             MapNode* mapNode, 
             Feature* feature, 
@@ -49,10 +53,14 @@ namespace osgEarth { namespace Annotation
 
         virtual ~FeatureNode() { }
 
+        /**
+         * The feature that this node will render.
+         */
         void setFeature( Feature* feature );
         Feature* getFeature() { return _feature.get(); }
         const Feature* getFeature() const { return _feature.get(); }
 
+        /** Whether the feature is draped on the terrain */
         bool isDraped() const { return _draped; }
 
         void init();
@@ -61,6 +69,8 @@ namespace osgEarth { namespace Annotation
 
         virtual osg::Group* getAttachPoint();
 
+        virtual void setStyle(const Style& style);
+
     public: // MapNodeObserver
 
         virtual void setMapNode( MapNode* mapNode );
@@ -71,11 +81,11 @@ namespace osgEarth { namespace Annotation
         virtual Config getConfig() const;
 
     protected:
-        osg::ref_ptr<Feature>       _feature;
-        GeometryCompilerOptions     _options;
-        bool                        _draped;
-        osg::Group*                 _attachPoint;
-        osg::Polytope               _featurePolytope;
+        osg::ref_ptr<Feature>        _feature;
+        GeometryCompilerOptions      _options;
+        bool                         _draped;
+        osg::Group*                  _attachPoint;
+        osg::Polytope                _featurePolytope;
 
         FeatureNode() { }
         FeatureNode(const FeatureNode& rhs, const osg::CopyOp& op) { }
diff --git a/src/osgEarthAnnotation/FeatureNode.cpp b/src/osgEarthAnnotation/FeatureNode.cpp
index 4186d58..abdd0ad 100644
--- a/src/osgEarthAnnotation/FeatureNode.cpp
+++ b/src/osgEarthAnnotation/FeatureNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
 
 #include <osgEarthSymbology/AltitudeSymbol>
 
+#include <osgEarth/ClampableNode>
 #include <osgEarth/DrapeableNode>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/Utils>
@@ -70,66 +71,90 @@ FeatureNode::init()
     if ( !getMapNode() )
         return;
 
-    // build the new feature geometry
+    if ( !_feature.valid() )
+        return;
+
+    // compilation options.
+    GeometryCompilerOptions options = _options;
+    
+    // figure out what kind of altitude manipulation we need to perform.
+    AnnotationUtils::AltitudePolicy ap;
+    AnnotationUtils::getAltitudePolicy( *_feature->style(), ap );
+
+    // If we're doing auto-clamping on the CPU, shut off compiler map clamping
+    // clamping since it would be redundant.
+    // TODO: I think this is OBE now that we have "scene" clamping technique..
+    if ( ap.sceneClamping )
     {
-        if ( _feature.valid() )
-        {
-            _feature->getWorldBoundingPolytope( getMapNode()->getMapSRS(), _featurePolytope );
-        }
+        options.ignoreAltitudeSymbol() = true;
+    }
+
+    // prep the compiler:
+    GeometryCompiler compiler( options );
+    Session* session = new Session( getMapNode()->getMap() );
+    GeoExtent extent(_feature->getSRS(), _feature->getGeometry()->getBounds());
+    osg::ref_ptr<FeatureProfile> profile = new FeatureProfile( extent );
+    FilterContext context( session, profile.get(), extent );
 
-        GeometryCompilerOptions options = _options;
-        
-        // have to disable compiler clamping if we're doing auto-clamping; especially
-        // in terrain-relative mode because the auto-clamper will think the clamped
-        // coords are the relative coords.
-        bool autoClamping = !_draped && supportsAutoClamping(*_feature->style());
-        if ( autoClamping )
+    // Clone the Feature before rendering as the GeometryCompiler and it's filters can change the coordinates
+    // of the geometry when performing localization or converting to geocentric.
+    osg::ref_ptr< Feature > clone = new Feature(*_feature.get(), osg::CopyOp::DEEP_COPY_ALL);
+
+    osg::Node* node = compiler.compile( clone.get(), *clone->style(), context );
+    if ( node )
+    {
+        if ( _feature->style().isSet() &&
+            AnnotationUtils::styleRequiresAlphaBlending( *_feature->style() ) &&
+            _feature->style()->get<ExtrusionSymbol>() )
         {
-            options.ignoreAltitudeSymbol() = true;
+            node = AnnotationUtils::installTwoPassAlpha( node );
         }
 
-        // prep the compiler:
-        GeometryCompiler compiler( options );
-        Session* session = new Session( getMapNode()->getMap() );
-        GeoExtent extent(_feature->getSRS(), _feature->getGeometry()->getBounds());
-        osg::ref_ptr<FeatureProfile> profile = new FeatureProfile( extent );
-        FilterContext context( session, profile.get(), extent );
+        _attachPoint = new osg::Group();
+        _attachPoint->addChild( node );
 
-        // Clone the Feature before rendering as the GeometryCompiler and it's filters can change the coordinates
-        // of the geometry when performing localization or converting to geocentric.
-        osg::ref_ptr< Feature > clone = new Feature(*_feature.get(), osg::CopyOp::DEEP_COPY_ALL);        
+        // Draped (projected) geometry
+        if ( ap.draping )
+        {
+            DrapeableNode* d = new DrapeableNode( getMapNode() );
+            d->addChild( _attachPoint );
+            this->addChild( d );
+        }
 
-        osg::Node* node = compiler.compile( clone.get(), *clone->style(), context );
-        if ( node )
+        // GPU-clamped geometry
+        else if ( ap.gpuClamping )
         {
-            if ( _feature->style().isSet() &&
-                AnnotationUtils::styleRequiresAlphaBlending( *_feature->style() ) &&
-                _feature->style()->get<ExtrusionSymbol>() )
+            ClampableNode* clampable = new ClampableNode( getMapNode() );
+            clampable->addChild( _attachPoint );
+            this->addChild( clampable );
+
+            const RenderSymbol* render = _feature->style()->get<RenderSymbol>();
+            if ( render && render->depthOffset().isSet() )
             {
-                node = AnnotationUtils::installTwoPassAlpha( node );
+                clampable->depthOffset() = *render->depthOffset();
             }
+        }
 
-            _attachPoint = new osg::Group();
-            _attachPoint->addChild( node );
+        else 
+        {
+            this->addChild( _attachPoint );
 
-            if ( _draped )
+            // CPU-clamped geometry?
+            if ( ap.sceneClamping )
             {
-                DrapeableNode* d = new DrapeableNode( getMapNode() );
-                d->addChild( _attachPoint );
-                this->addChild( d );
-            }
-            else
-            {
-                this->addChild( _attachPoint );
-            }
+                // save for later when we need to reclamp the mesh on the CPU
+                _altitude = _feature->style()->get<AltitudeSymbol>();
 
-            // workaround until we can auto-clamp extruded/sub'd geometries.
-            if ( autoClamping )
-            {
-                applyStyle( *_feature->style() );
+                // The polytope will ensure we only clamp to intersecting tiles:
+                _feature->getWorldBoundingPolytope( getMapNode()->getMapSRS(), _featurePolytope );
 
+                // activate the terrain callback:
+                setCPUAutoClamping( true );
+
+                // set default lighting based on whether we are extruding:
                 setLightingIfNotSet( _feature->style()->has<ExtrusionSymbol>() );
 
+                // do an initial clamp to get started.
                 clampMesh( getMapNode()->getTerrain()->getGraph() );
             }
         }
@@ -153,6 +178,16 @@ FeatureNode::setFeature( Feature* feature )
     init();
 }
 
+void
+FeatureNode::setStyle(const Style& style)
+{
+    if ( _feature.valid() )
+    {
+        _feature->style() = style;
+        init();
+    }
+}
+
 osg::Group*
 FeatureNode::getAttachPoint()
 {
@@ -168,6 +203,8 @@ FeatureNode::getAttachPoint()
     return _attachPoint;
 }
 
+
+// This will be called by AnnotationNode when a new terrain tile comes in.
 void
 FeatureNode::reclamp( const TileKey& key, osg::Node* tile, const Terrain* )
 {
diff --git a/src/osgEarthAnnotation/HighlightDecoration b/src/osgEarthAnnotation/HighlightDecoration
index 961c437..2a3f64c 100644
--- a/src/osgEarthAnnotation/HighlightDecoration
+++ b/src/osgEarthAnnotation/HighlightDecoration
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/HighlightDecoration.cpp b/src/osgEarthAnnotation/HighlightDecoration.cpp
index a09f8c0..8267196 100644
--- a/src/osgEarthAnnotation/HighlightDecoration.cpp
+++ b/src/osgEarthAnnotation/HighlightDecoration.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@ namespace
 
         void traverse(osg::NodeVisitor& nv)
         {
-            osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
+            osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
             if ( cv && _fillNode.valid() && _pass1.valid() )
             {
                 const osg::GraphicsContext* gc = cv->getCurrentCamera()->getGraphicsContext();
diff --git a/src/osgEarthAnnotation/ImageOverlay b/src/osgEarthAnnotation/ImageOverlay
index aa6b8d3..53fae48 100644
--- a/src/osgEarthAnnotation/ImageOverlay
+++ b/src/osgEarthAnnotation/ImageOverlay
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -47,6 +47,9 @@ namespace osgEarth { namespace Annotation
             CONTROLPOINT_UPPER_RIGHT
         };
         
+        /**
+         * Constructs an image overlay.
+         */
         ImageOverlay(MapNode* mapNode, osg::Image* image = NULL);
 
         /**
diff --git a/src/osgEarthAnnotation/ImageOverlay.cpp b/src/osgEarthAnnotation/ImageOverlay.cpp
index dd17251..32d8749 100644
--- a/src/osgEarthAnnotation/ImageOverlay.cpp
+++ b/src/osgEarthAnnotation/ImageOverlay.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
 #include <osgEarth/VirtualProgram>
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
+#include <osgEarth/ShaderGenerator>
 #include <osg/Geode>
 #include <osg/ShapeDrawable>
 #include <osg/Texture2D>
@@ -193,16 +194,19 @@ ImageOverlay::postCTOR()
 
     d->addChild( _transform );
 
+    init();
+
     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 );
+        ShaderGenerator gen;
+        d->accept( gen );
+        //// 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 );
 }
 
diff --git a/src/osgEarthAnnotation/ImageOverlayEditor b/src/osgEarthAnnotation/ImageOverlayEditor
index 5e76872..062cd3c 100644
--- a/src/osgEarthAnnotation/ImageOverlayEditor
+++ b/src/osgEarthAnnotation/ImageOverlayEditor
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/ImageOverlayEditor.cpp b/src/osgEarthAnnotation/ImageOverlayEditor.cpp
index 5977156..2e0008d 100644
--- a/src/osgEarthAnnotation/ImageOverlayEditor.cpp
+++ b/src/osgEarthAnnotation/ImageOverlayEditor.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/LabelNode b/src/osgEarthAnnotation/LabelNode
index 7fcd158..ea217fc 100644
--- a/src/osgEarthAnnotation/LabelNode
+++ b/src/osgEarthAnnotation/LabelNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
 #include <osgEarthAnnotation/OrthoNode>
 #include <osgEarthSymbology/Style>
 #include <osgEarth/MapNode>
+#include <osg/Geode>
 
 namespace osgEarth { namespace Annotation
 {
@@ -65,6 +66,13 @@ namespace osgEarth { namespace Annotation
             const Style&            style );
 
         /**
+         * Construct a label node with just a style
+         */
+        LabelNode(
+            MapNode*                mapNode,
+            const Style&            style );
+
+        /**
          * Constructs a label node that is positioned some other way,
          * typically by being in a subgraph under another transform
          * somewhere.
@@ -92,7 +100,7 @@ namespace osgEarth { namespace Annotation
         /**
          * Gets a copy of the text style.
          */
-        const Style style() const { return _style; }
+        const Style& style() const { return _style; }
 
         /**
          * Sets a new text style
@@ -110,9 +118,9 @@ namespace osgEarth { namespace Annotation
     protected:
         void init(const Style& style);
 
-        std::string       _text;
-        class osg::Geode* _geode;
-        Style             _style;
+        std::string              _text;
+        Style                    _style;
+        osg::ref_ptr<osg::Geode> _geode;
 
         /** Copy constructor */
         LabelNode( const LabelNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
diff --git a/src/osgEarthAnnotation/LabelNode.cpp b/src/osgEarthAnnotation/LabelNode.cpp
index b57dd2d..65b6c85 100644
--- a/src/osgEarthAnnotation/LabelNode.cpp
+++ b/src/osgEarthAnnotation/LabelNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -44,8 +44,7 @@ LabelNode::LabelNode(MapNode*            mapNode,
                      const Style&        style ) :
 
 OrthoNode( mapNode, position ),
-_text    ( text ),
-_geode   ( 0L )
+_text    ( text )
 {
     init( style );
 }
@@ -56,8 +55,7 @@ LabelNode::LabelNode(MapNode*            mapNode,
                      const TextSymbol*   symbol ) :
 
 OrthoNode( mapNode, position ),
-_text    ( text ),
-_geode   ( 0L )
+_text    ( text )
 {
     Style style;
     style.add( const_cast<TextSymbol*>(symbol) );
@@ -67,8 +65,7 @@ _geode   ( 0L )
 LabelNode::LabelNode(const std::string&  text,
                      const Style&        style ) :
 OrthoNode(),
-_text    ( text ),
-_geode   ( 0L )
+_text    ( text )
 {
     init( style );
 }
@@ -76,8 +73,14 @@ _geode   ( 0L )
 LabelNode::LabelNode(MapNode*            mapNode,
                      const GeoPoint&     position,
                      const Style&        style ) :
-OrthoNode( mapNode, position ),
-_geode   ( 0L )
+OrthoNode( mapNode, position )
+{
+    init( style );
+}
+
+LabelNode::LabelNode(MapNode*            mapNode,
+                     const Style&        style ) :
+OrthoNode( mapNode, GeoPoint::INVALID )
 {
     init( style );
 }
@@ -86,19 +89,12 @@ void
 LabelNode::init( const Style& style )
 {
     _geode = new osg::Geode();
-    getAttachPoint()->addChild( _geode );
+    getAttachPoint()->addChild( _geode.get() );
 
     osg::StateSet* stateSet = _geode->getOrCreateStateSet();
     stateSet->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), 1 );
 
     setStyle( style );
-
-    applyStyle( style );
-
-    setLightingIfNotSet( false );
-
-    ShaderGenerator gen( Registry::stateSetCache() );
-    this->accept( gen );
 }
 
 void
@@ -115,6 +111,7 @@ LabelNode::setText( const std::string& text )
     {
         d->setText( text );
         d->dirtyDisplayList();
+        _text = text;
     }
 }
 
@@ -126,6 +123,8 @@ LabelNode::setStyle( const Style& style )
         OE_WARN << LC << "Illegal state: cannot change a LabelNode that is not dynamic" << std::endl;
         return;
     }
+    
+    this->clearDecoration();
 
     _geode->removeDrawables( 0, _geode->getNumDrawables() );
 
@@ -142,6 +141,9 @@ LabelNode::setStyle( const Style& style )
     applyStyle( _style );
 
     setLightingIfNotSet( false );
+
+    ShaderGenerator gen( Registry::stateSetCache() );
+    this->accept( gen );
 }
 
 void
diff --git a/src/osgEarthAnnotation/LocalGeometryNode b/src/osgEarthAnnotation/LocalGeometryNode
index 1c8ca04..459317f 100644
--- a/src/osgEarthAnnotation/LocalGeometryNode
+++ b/src/osgEarthAnnotation/LocalGeometryNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -43,8 +43,7 @@ namespace osgEarth { namespace Annotation
         LocalGeometryNode( 
             MapNode*     mapNode,
             Geometry*    geom, 
-            const Style& style, 
-            bool         draped     =false );
+            const Style& style );
 
         /**
          * Construcst a new localized node that renders a pre-built
@@ -53,13 +52,45 @@ namespace osgEarth { namespace Annotation
         LocalGeometryNode( 
             MapNode*     mapNode,
             osg::Node*   node, 
-            const Style& style      =Style(), 
-            bool         draped     =false );
+            const Style& style  =Style() );
 
         virtual ~LocalGeometryNode() { }
 
+        /**
+         * Gets the Style used to create thenode.
+         */
+        const Style& getStyle() const { return _style; }
+
+        /**
+         * Sets a new style for the node
+         */
+        void setStyle(const Style& style);
+
+        /**
+         * Gets the geometry used to create this node (if applicable)
+         */
+        const Geometry* getGeometry() const { return _geom.get(); }
+
+        /**
+         * Sets new geometry for this node.
+         */
+        void setGeometry( Geometry* geom );
+
+        /**
+         * Gets the external node attached to this annotation (if applicable)
+         */
+        osg::Node* getNode() { return _node; }
+
+        /**
+         * Sets an external node to attach to this annotation
+         */
+        void setNode( osg::Node* node );
+
     public:
 
+        /**
+         * Constructs an LGN from a serialized Config.
+         */
         LocalGeometryNode(
             MapNode*              mapNode,
             const Config&         conf,
@@ -67,12 +98,20 @@ namespace osgEarth { namespace Annotation
 
         virtual Config getConfig() const;
 
+    protected: // LocalizedNode
+
+        virtual osg::MatrixTransform* getTransform() { return _xform.get(); }
+
     protected:
 
-        optional<Style> _style;
-        bool  _draped;
-        osg::ref_ptr<Geometry> _geom;
+        Style                        _style;
+        osg::ref_ptr<osg::Node>      _node;
+        osg::ref_ptr<Geometry>       _geom;
+
+        osg::ref_ptr<osg::MatrixTransform> _xform;
 
+        void initNode();
+        void initGeometry(const osgDB::Options*);
         void init(const osgDB::Options*);
     };
 
diff --git a/src/osgEarthAnnotation/LocalGeometryNode.cpp b/src/osgEarthAnnotation/LocalGeometryNode.cpp
index dfe2f2f..5362df2 100644
--- a/src/osgEarthAnnotation/LocalGeometryNode.cpp
+++ b/src/osgEarthAnnotation/LocalGeometryNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -34,65 +34,118 @@ using namespace osgEarth::Features;
 
 LocalGeometryNode::LocalGeometryNode(MapNode*     mapNode,
                                      Geometry*    geom,
-                                     const Style& style,
-                                     bool         draped ) :
+                                     const Style& style) :
 LocalizedNode( mapNode ),
 _geom        ( geom ),
-_draped      ( draped )
+_style       ( style )
 {
-    _style = style;
+    _xform = new osg::MatrixTransform();
     init( 0L );
 }
 
 
 LocalGeometryNode::LocalGeometryNode(MapNode*     mapNode,
-                                     osg::Node*   content,
-                                     const Style& style,
-                                     bool         draped ) :
+                                     osg::Node*   node,
+                                     const Style& style) :
 LocalizedNode( mapNode ),
-_draped      ( draped )
+_node        ( node ),
+_style       ( style )
 {
-    _style = style;
+    _xform = new osg::MatrixTransform();
+    init( 0L );
+}
 
-    if ( content )
-    {
-        getChildAttachPoint()->addChild( content );
-        getDrapeable()->setDraped( _draped );
 
-        this->addChild( getRoot() );
+void
+LocalGeometryNode::initNode()
+{
+    // reset
+    osgEarth::clearChildren( this );
+    osgEarth::clearChildren( _xform.get() );
+    this->addChild( _xform.get() );
 
-        // this will activate the clamping logic
-        applyStyle( style );
+    if ( _node.valid() )
+    {
+        _xform->addChild( _node );
+        // activate clamping if necessary
+        replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) );
 
-        setLightingIfNotSet( style.has<ExtrusionSymbol>() );
+        applyGeneralSymbology( _style );
+        setLightingIfNotSet( _style.has<ExtrusionSymbol>() );
     }
 }
 
 
 void
-LocalGeometryNode::init(const osgDB::Options* dbOptions)
+LocalGeometryNode::initGeometry(const osgDB::Options* dbOptions)
 {
+    // reset
+    osgEarth::clearChildren( this );
+    osgEarth::clearChildren( _xform.get() );
+    this->addChild( _xform.get() );
+
     if ( _geom.valid() )
     {
-        osg::ref_ptr<Feature> feature = new Feature( _geom.get(), 0L );
-        feature->style() = *_style;
+        Session* session = 0L;
+        if ( getMapNode() )
+            session = new Session(getMapNode()->getMap(), 0L, 0L, dbOptions);
+        FilterContext cx( session );
 
-        GeometryCompiler compiler;
-        FilterContext cx( getMapNode() ? new Session(getMapNode()->getMap()) : 0L );
-        osg::Node* node = compiler.compile( feature.get(), cx );
+        GeometryCompiler gc;
+        osg::Node* node = gc.compile( _geom.get(), _style, cx );
         if ( node )
         {
-            getChildAttachPoint()->addChild( node );
-            getDrapeable()->setDraped( _draped );
-            this->addChild( getRoot() );
+            _xform->addChild( node );
+            // activate clamping if necessary
+            replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) );
 
-            // prep for clamping
-            applyStyle( *_style );
+            applyGeneralSymbology( _style );
         }
     }
 }
 
 
+void 
+LocalGeometryNode::init(const osgDB::Options* options )
+{
+    this->clearDecoration();
+    
+    if ( _node.valid() )
+    {
+        initNode();
+    }
+    else
+    {
+        initGeometry( options );
+    }
+}
+
+
+void
+LocalGeometryNode::setStyle( const Style& style )
+{
+    _style = style;
+    init( 0L );
+}
+
+
+void
+LocalGeometryNode::setNode( osg::Node* node )
+{
+    _node = node;
+    _geom = 0L;
+    initNode();
+}
+
+
+void
+LocalGeometryNode::setGeometry( Geometry* geom )
+{
+    _geom = geom;
+    _node = 0L;
+    initGeometry(0L);
+}
+
 //-------------------------------------------------------------------
 
 OSGEARTH_REGISTER_ANNOTATION( local_geometry, osgEarth::Annotation::LocalGeometryNode );
@@ -103,6 +156,8 @@ LocalGeometryNode::LocalGeometryNode(MapNode*              mapNode,
                                      const osgDB::Options* dbOptions) :
 LocalizedNode( mapNode, conf )
 {
+    _xform = new osg::MatrixTransform();
+
     if ( conf.hasChild("geometry") )
     {
         Config geomconf = conf.child("geometry");
@@ -110,7 +165,6 @@ LocalizedNode( mapNode, conf )
         if ( _geom.valid() )
         {
             conf.getObjIfSet( "style", _style );
-            _draped = conf.value<bool>("draped",false);
 
             init( dbOptions );
 
@@ -128,7 +182,8 @@ LocalGeometryNode::getConfig() const
     if ( _geom.valid() )
     {
         conf.add( Config("geometry", GeometryUtils::geometryToWKT(_geom.get())) );
-        conf.addObjIfSet( "style", _style );
+        if ( !_style.empty() )
+            conf.addObj( "style", _style );
         conf.addObj( "position", getPosition() );
     }
     else
diff --git a/src/osgEarthAnnotation/LocalizedNode b/src/osgEarthAnnotation/LocalizedNode
index fc7c3da..5472d32 100644
--- a/src/osgEarthAnnotation/LocalizedNode
+++ b/src/osgEarthAnnotation/LocalizedNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -20,9 +20,9 @@
 #define OSGEARTH_ANNO_LOCALIZED_NODE_H 1
 
 #include <osgEarthAnnotation/AnnotationNode>
-#include <osgEarth/DrapeableNode>
+#include <osgEarth/OverlayNode>
 #include <osgEarth/GeoData>
-#include <osg/Transform>
+#include <osg/MatrixTransform>
 
 namespace osgEarth { namespace Annotation
 {	
@@ -30,30 +30,28 @@ namespace osgEarth { namespace Annotation
 
     /**
      * Base class for node that is localized at a specific geographic point.
+     *
+     * Don't use this class *directly*. If you are trying to add your own 
+     * osg::Node as an Annotation, use the LocalizedGeometryNode instead.
      */
     class OSGEARTHANNO_EXPORT LocalizedNode : public PositionedAnnotationNode
     {
     public:
-        META_AnnotationNode(osgEarthAnnotation, LocalizedNode);
+        META_AnnotationNodeAbstract(osgEarthAnnotation, LocalizedNode);
 
         /**
          * Constructs a new LocalizedNode.
-         *
          * @param mapSRS  Spatial reference for position info
          * @param pos     Initial position of the node (map coords)
-         * @param is2D    Whether to autotransform the node to face/scale to the screen
          */
         LocalizedNode( 
             MapNode*          mapNode,
-            const GeoPoint&   pos      =GeoPoint::INVALID,
-            bool              is2D     =false );
+            const GeoPoint&   pos      =GeoPoint::INVALID );
 
         LocalizedNode(
             MapNode*          mapNode,
             const Config&     conf );
 
-        virtual ~LocalizedNode() { }
-
 
     public: // PositionedAnnotationNode
 
@@ -63,14 +61,11 @@ namespace osgEarth { namespace Annotation
         /** Gets the node position (in map coords) */
         virtual GeoPoint getPosition() const;
 
+
     public: // MapNodeObserver
 
         virtual void setMapNode( MapNode* mapNode );
 
-    public: // osg::Node
-
-        virtual void traverse( osg::NodeVisitor& nv );
-
     public:
         /**
          * Sets a "local offset" position - for an ECEF map, this is an offset in 
@@ -95,48 +90,62 @@ namespace osgEarth { namespace Annotation
         const osg::Quat& getLocalRotation() const;
 
         /**
+         * Local scale factor.
+         */
+        void setScale( const osg::Vec3f& scale );
+        const osg::Vec3f& getScale() const { return _scale; }
+
+        /**
          * Enables or disable automatic horizon culling
          */
         void setHorizonCulling( bool value );
         bool getHorizonCulling() const;
 
-        void setScale( const osg::Vec3f& scale );
 
-        const osg::Vec3f& getScale() const { return _scale; }
+    public: // osg::Node
 
-        virtual osg::Group* getRoot() { return _draper.get(); }
+        virtual osg::BoundingSphere computeBound() const;
 
-        virtual osg::Group* getChildAttachPoint() { return _xform.get(); }
 
-        virtual osg::Transform* getTransform() { return _xform.get(); }
+    protected:
+
+        /**
+         * The matrix transform that controls this node's position
+         */
+        virtual osg::MatrixTransform* getTransform() =0;
 
-        virtual DrapeableNode* getDrapeable() { return _draper.get(); }
 
     protected:
-        osg::ref_ptr<osg::Transform> _xform;
-        osg::ref_ptr<DrapeableNode> _draper;
-        bool _horizonCulling;
-        bool _autoTransform;
-        GeoPoint _mapPosition;
-        osg::Vec3f _scale;
-        osg::Vec3d _localOffset;
-        osg::Quat  _localRotation;
+        bool                               _initComplete;
+        bool                               _horizonCulling;
+        GeoPoint                           _mapPosition;
+        osg::Vec3f                         _scale;
+        osg::Vec3d                         _localOffset;
+        osg::Quat                          _localRotation;
+        osg::ref_ptr<osg::NodeCallback>    _cullByHorizonCB;
 
         friend class Decoration;
         
+        // re-clamped the vert mesh based on a new terrain tile coming in
         virtual void reclamp( const TileKey& key, osg::Node* tile, const Terrain* terrain );
 
-            
-        // copy constructor
+        // refreshed the main transform with data from an asbolute point
+        bool updateTransform(const GeoPoint& absPt, osg::Node* patch =0L);
+
+        // checks for overlay requirements, and if needed, installs a decorator node above
+        // the passed-in node to facilitate the clamping/draping. The proper usage pattern
+        // is:  node = applyAltitudePolicy(node, style)
+        osg::Node* applyAltitudePolicy( osg::Node* node, const Style& style );
+
+        // hidden copy constructor
         LocalizedNode() { }
         LocalizedNode(const LocalizedNode& rhs, const osg::CopyOp& op=osg::CopyOp::DEEP_COPY_ALL) { }
+        virtual ~LocalizedNode() { }
 
-        bool updateTransforms(const GeoPoint& absPt, osg::Node* patch =0L);
 
     private:
 
-        void init( MapNode* mapNode, const GeoPoint& position );
-        
+        void init();
     };
 
 } } // namespace osgEarth::Annotation
diff --git a/src/osgEarthAnnotation/LocalizedNode.cpp b/src/osgEarthAnnotation/LocalizedNode.cpp
index eef0fb2..5885fc8 100644
--- a/src/osgEarthAnnotation/LocalizedNode.cpp
+++ b/src/osgEarthAnnotation/LocalizedNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,10 +18,11 @@
 */
 
 #include <osgEarthAnnotation/LocalizedNode>
-#include <osgEarthAnnotation/Decluttering>
+#include <osgEarthAnnotation/AnnotationUtils>
+#include <osgEarth/ClampableNode>
+#include <osgEarth/DrapeableNode>
 #include <osgEarth/CullingUtils>
 #include <osgEarth/MapNode>
-#include <osg/AutoTransform>
 #include <osg/MatrixTransform>
 
 #define LC "[LocalizedNode] "
@@ -31,54 +32,57 @@ using namespace osgEarth::Annotation;
 
 
 LocalizedNode::LocalizedNode(MapNode*        mapNode,
-                             const GeoPoint& position,
-                             bool            is2D ) :
+                             const GeoPoint& position) :
 PositionedAnnotationNode( mapNode ),
-_horizonCulling         ( false ),
-_autoTransform          ( is2D ),
-_scale                  ( 1.0f, 1.0f, 1.0f )
+_initComplete           ( false ),
+_horizonCulling         ( true ),
+_scale                  ( 1.0f, 1.0f, 1.0f ),
+_mapPosition            ( position )
 {
-    init( mapNode, position );
+    init();
 }
 
 LocalizedNode::LocalizedNode(MapNode* mapNode, const Config& conf) :
-PositionedAnnotationNode(mapNode, conf),
-_horizonCulling         ( false ),
-_autoTransform          ( false ),
+PositionedAnnotationNode( mapNode, conf ),
+_initComplete           ( false ),
+_horizonCulling         ( true ),
 _scale                  ( 1.0f, 1.0f, 1.0f )
 {
-    init( mapNode, GeoPoint::INVALID );
+    init();
 }
 
 
 void
-LocalizedNode::init( MapNode* mapNode, const GeoPoint& position )
+LocalizedNode::init()
 {
-    if ( _autoTransform )
-    {
-        osg::AutoTransform* at = new osg::AutoTransform();
-        at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN );
-        at->setAutoScaleToScreen( true );
-        at->setCullingActive( false ); // just for the first pass
-        _xform = at;
-    }
-    else
-    {
-        _xform = new osg::MatrixTransform();
+    this->getOrCreateStateSet()->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
+}
 
-        this->getOrCreateStateSet()->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
-    }
-    
-    //this->getOrCreateStateSet()->setMode( GL_BLEND, 1 );
 
-    setHorizonCulling( true );
+namespace
+{
+    static Threading::Mutex s_initCompleteMutex;
+}
 
-    _draper = new DrapeableNode( mapNode, false );
-    _draper->addChild( _xform.get() );
-    
-    setPosition( position );
+osg::BoundingSphere
+LocalizedNode::computeBound() const
+{
+    if ( !_initComplete )
+    {
+        // perform initialization that cannot happen in the CTOR
+        // (due to possible virtual function calls)
+        Threading::ScopedMutexLock lock(s_initCompleteMutex);
+        if ( !_initComplete )
+        {
+            const_cast<LocalizedNode*>(this)->_initComplete = true;
+            const_cast<LocalizedNode*>(this)->setHorizonCulling( _horizonCulling );
+            const_cast<LocalizedNode*>(this)->setPosition      ( _mapPosition );
+        }
+    }
+    return PositionedAnnotationNode::computeBound();
 }
 
+
 void
 LocalizedNode::setMapNode( MapNode* mapNode )
 {
@@ -94,58 +98,46 @@ LocalizedNode::setMapNode( MapNode* mapNode )
         }
 
         // re-apply the position since the map has changed
-        setPosition( getPosition() );
+        setPosition( _mapPosition );
     }
 }
 
-void
-LocalizedNode::traverse( osg::NodeVisitor& nv )
-{
-    if ( _autoTransform && nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
-    {
-       _xform->setCullingActive( true );
-    }
-    
-    AnnotationNode::traverse( nv );
-}
 
 bool
 LocalizedNode::setPosition( const GeoPoint& pos )
 {
-    if ( getMapNode() )
+    if ( _initComplete )
     {
-        // first transform the point to the map's SRS:
-        const SpatialReference* mapSRS = getMapNode()->getMapSRS();
-        GeoPoint mapPos = mapSRS ? pos.transform(mapSRS) : pos;
-        if ( !mapPos.isValid() )
-            return false;
+        if ( getMapNode() )
+        {
+            // first transform the point to the map's SRS:
+            const SpatialReference* mapSRS = getMapNode()->getMapSRS();
+            GeoPoint mapPos = mapSRS ? pos.transform(mapSRS) : pos;
+            if ( !mapPos.isValid() )
+                return false;
+
+            _mapPosition = mapPos;
+        }
+        else
+        {
+            _mapPosition = pos;
+        }
+
+        // make sure the node is set up for auto-z-update if necessary:
+        configureForAltitudeMode( _mapPosition.altitudeMode() );
 
-        _mapPosition = mapPos;
+        // update the node.
+        return updateTransform( _mapPosition );
     }
     else
     {
         _mapPosition = pos;
+        return true;
     }
-
-    // make sure the node is set up for auto-z-update if necessary:
-    configureForAltitudeMode( _mapPosition.altitudeMode() );
-
-    // update the node.
-    if ( !updateTransforms( _mapPosition ) )
-        return false;
-
-    return true;
-}
-
-void
-LocalizedNode::setScale( const osg::Vec3f& scale )
-{
-    _scale = scale;
-    updateTransforms( getPosition() );
 }
 
 bool
-LocalizedNode::updateTransforms( const GeoPoint& p, osg::Node* patch )
+LocalizedNode::updateTransform( const GeoPoint& p, osg::Node* patch )
 {
     if ( p.isValid() )
     {
@@ -162,42 +154,19 @@ LocalizedNode::updateTransforms( const GeoPoint& p, osg::Node* patch )
         // apply the local offsets
         local2world.preMult( osg::Matrix::translate(_localOffset) );
 
-        if ( _autoTransform )
-        {
-            static_cast<osg::AutoTransform*>(_xform.get())->setPosition( local2world.getTrans() );
-            static_cast<osg::AutoTransform*>(_xform.get())->setScale( _scale );
-            static_cast<osg::AutoTransform*>(_xform.get())->setRotation( _localRotation );
-        }
-        else
-        {
-            static_cast<osg::MatrixTransform*>(_xform.get())->setMatrix( 
-                osg::Matrix::scale(_scale) * 
-                osg::Matrix::rotate(_localRotation) *
-                local2world  );
-        }
-
-        
-        CullNodeByHorizon* culler = dynamic_cast<CullNodeByHorizon*>(_xform->getCullCallback());
-        if ( culler )
-            culler->_world = local2world.getTrans();
+        getTransform()->setMatrix( 
+            osg::Matrix::scale (_scale)         * 
+            osg::Matrix::rotate(_localRotation) *
+            local2world  );
     }
     else
     {
         osg::Vec3d absPos = p.vec3d() + _localOffset;
 
-        if ( _autoTransform )
-        {
-            static_cast<osg::AutoTransform*>(_xform.get())->setPosition( absPos );
-            static_cast<osg::AutoTransform*>(_xform.get())->setScale( _scale );
-            static_cast<osg::AutoTransform*>(_xform.get())->setRotation( _localRotation );
-        }
-        else
-        {
-            static_cast<osg::MatrixTransform*>(_xform.get())->setMatrix(
-                osg::Matrix::scale(_scale) * 
-                osg::Matrix::rotate(_localRotation) *
-                osg::Matrix::translate(absPos) );
-        }
+        getTransform()->setMatrix(
+            osg::Matrix::scale    (_scale)         * 
+            osg::Matrix::rotate   (_localRotation) *
+            osg::Matrix::translate(absPos) );
     }
     
 
@@ -213,6 +182,13 @@ LocalizedNode::getPosition() const
 }
 
 void
+LocalizedNode::setScale( const osg::Vec3f& scale )
+{
+    _scale = scale;
+    updateTransform( _mapPosition );
+}
+
+void
 LocalizedNode::setLocalOffset( const osg::Vec3d& offset )
 {
     _localOffset = offset;
@@ -241,23 +217,23 @@ LocalizedNode::getLocalRotation() const
 void
 LocalizedNode::setHorizonCulling( bool value )
 {
-    if ( _horizonCulling != value )
-    {
-        _horizonCulling = value;
+    _horizonCulling = value;
 
-        if ( _horizonCulling && getMapNode() && getMapNode()->isGeocentric() )
+    if ( _initComplete && getMapNode() && getMapNode()->isGeocentric() )
+    {
+        if ( _horizonCulling && !_cullByHorizonCB.valid() )
         {
-            osg::Vec3d world;
-            if ( _autoTransform )
-                world = static_cast<osg::AutoTransform*>(_xform.get())->getPosition();
-            else
-                world = static_cast<osg::MatrixTransform*>(_xform.get())->getMatrix().getTrans();
+            _cullByHorizonCB = new CullNodeByHorizon(
+                getTransform(),
+                getMapNode()->getMapSRS()->getEllipsoid() );
 
-            _xform->setCullCallback( new CullNodeByHorizon(world, getMapNode()->getMapSRS()->getEllipsoid()) );
+            addCullCallback( _cullByHorizonCB.get() );
         }
-        else
+
+        else if ( !_horizonCulling && _cullByHorizonCB.valid() )
         {
-            _xform->removeCullCallback( _xform->getCullCallback() );
+            removeCullCallback( _cullByHorizonCB.get() );
+            _cullByHorizonCB = 0L;
         }
     }
 }
@@ -268,6 +244,49 @@ LocalizedNode::reclamp( const TileKey& key, osg::Node* tile, const Terrain* terr
     // first verify that the control position intersects the tile:
     if ( key.getExtent().contains( _mapPosition.x(), _mapPosition.y() ) )
     {
-        updateTransforms(_mapPosition, tile);
+        updateTransform(_mapPosition, tile);
+    }
+}
+
+
+osg::Node*
+LocalizedNode::applyAltitudePolicy(osg::Node* node, const Style& style)
+{
+    AnnotationUtils::AltitudePolicy ap;
+    AnnotationUtils::getAltitudePolicy( style, ap );
+
+    // Draped (projected) geometry
+    if ( ap.draping )
+    {
+        DrapeableNode* drapable = new DrapeableNode( getMapNode() );
+        drapable->addChild( node );
+        node = drapable;
+    }
+
+    // gw - not sure whether is makes sense to support this for LocalizedNode
+    // GPU-clamped geometry
+    else if ( ap.gpuClamping )
+    {
+        ClampableNode* clampable = new ClampableNode( getMapNode() );
+        clampable->addChild( node );
+        node = clampable;
+
+        const RenderSymbol* render = style.get<RenderSymbol>();
+        if ( render && render->depthOffset().isSet() )
+        {
+            clampable->depthOffset() = *render->depthOffset();
+        }
+    }
+
+    // scenegraph-clamped geometry
+    else if ( ap.sceneClamping )
+    {
+        // save for later when we need to reclamp the mesh on the CPU
+        _altitude = style.get<AltitudeSymbol>();
+
+        // activate the terrain callback:
+        setCPUAutoClamping( true );
     }
+
+    return node;
 }
diff --git a/src/osgEarthAnnotation/ModelNode b/src/osgEarthAnnotation/ModelNode
index f139682..c5f7305 100644
--- a/src/osgEarthAnnotation/ModelNode
+++ b/src/osgEarthAnnotation/ModelNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -19,7 +19,7 @@
 #ifndef OSGEARTH_ANNO_MODEL_NODE_H
 #define OSGEARTH_ANNO_MODEL_NODE_H 1
 
-#include <osgEarthAnnotation/LocalGeometryNode>
+#include <osgEarthAnnotation/LocalizedNode>
 #include <osgEarth/URI>
 #include <osgEarth/CachePolicy>
 #include <osgDB/ReaderWriter>
@@ -45,10 +45,23 @@ namespace osgEarth { namespace Annotation
             const Style&          style,
             const osgDB::Options* dbOptions   =0L );
 
-        virtual ~ModelNode() { }
+        /**
+         * Sets a new style and rebuilds the node.
+         */
+        void setStyle( const Style& style );
+
+        /**
+         * Gets the style last used to build this node.
+         */
+        const Style& getStyle() const { return _style; }
+
 
     public:
 
+        /**
+         * Constructs a model node from a serialized Config.
+         * (internal method).
+         */
         ModelNode(
             MapNode*              mapNode,
             const Config&         conf,
@@ -56,11 +69,19 @@ namespace osgEarth { namespace Annotation
 
         virtual Config getConfig() const;
 
+    protected: // LocalizedNode
+
+        virtual osg::MatrixTransform* getTransform() { return _xform.get(); }
+
     protected:
 
-        optional<Style> _style;
+        Style _style;
+        osg::ref_ptr<const osgDB::Options> _dbOptions;
+        osg::ref_ptr<osg::MatrixTransform> _xform;
+
+        void init();
 
-        void init(const osgDB::Options*);
+        virtual ~ModelNode() { }
     };
 
 } } // namespace osgEarth::Annotation
diff --git a/src/osgEarthAnnotation/ModelNode.cpp b/src/osgEarthAnnotation/ModelNode.cpp
index f136619..3f1d384 100644
--- a/src/osgEarthAnnotation/ModelNode.cpp
+++ b/src/osgEarthAnnotation/ModelNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -39,24 +39,40 @@ using namespace osgEarth::Symbology;
 ModelNode::ModelNode(MapNode*              mapNode,
                      const Style&          style,
                      const osgDB::Options* dbOptions ) :
-LocalizedNode( mapNode )
+LocalizedNode( mapNode ),
+_style       ( style ),
+_dbOptions   ( dbOptions )
+{
+    _xform = new osg::MatrixTransform();
+    init();
+}
+
+
+void
+ModelNode::setStyle(const Style& style)
 {
     _style = style;
-    init( dbOptions );
+    init();
 }
 
 
 void
-ModelNode::init(const osgDB::Options* dbOptions)
+ModelNode::init()
 {
+    // reset.
+    this->clearDecoration();
+    osgEarth::clearChildren( this );
+    osgEarth::clearChildren( _xform.get() );
+    this->addChild( _xform.get() );
+
     this->setHorizonCulling(false);
 
-    osg::ref_ptr<const ModelSymbol> sym = _style->get<ModelSymbol>();
+    osg::ref_ptr<const ModelSymbol> sym = _style.get<ModelSymbol>();
     
     // backwards-compatibility: support for MarkerSymbol (deprecated)
-    if ( !sym.valid() && _style->has<MarkerSymbol>() )
+    if ( !sym.valid() && _style.has<MarkerSymbol>() )
     {
-        osg::ref_ptr<InstanceSymbol> temp = _style->get<MarkerSymbol>()->convertToInstanceSymbol();
+        osg::ref_ptr<InstanceSymbol> temp = _style.get<MarkerSymbol>()->convertToInstanceSymbol();
         sym = dynamic_cast<const ModelSymbol*>( temp.get() );
     }
 
@@ -74,12 +90,12 @@ ModelNode::init(const osgDB::Options* dbOptions)
 
                 if ( sym->uriAliasMap()->empty() )
                 {
-                    node = uri.getNode( dbOptions );
+                    node = uri.getNode( _dbOptions.get() );
                 }
                 else
                 {
                     // install an alias map if there's one in the symbology.
-                    osg::ref_ptr<osgDB::Options> tempOptions = Registry::instance()->cloneOrCreateOptions(dbOptions);
+                    osg::ref_ptr<osgDB::Options> tempOptions = Registry::instance()->cloneOrCreateOptions(_dbOptions.get());
                     tempOptions->setReadFileCallback( new URIAliasMapReadCallback(*sym->uriAliasMap(), uri.full()) );
                     node = uri.getNode( tempOptions.get() );
                 }
@@ -99,16 +115,20 @@ ModelNode::init(const osgDB::Options* dbOptions)
                     node->accept( gen );
 
                     // need a top-level shader too:
-                    VirtualProgram* vp = new VirtualProgram();
-                    vp->installDefaultColoringAndLightingShaders();
-                    this->getOrCreateStateSet()->setAttributeAndModes( vp, 1 );
+                    // gw: why?
+                    //VirtualProgram* vp = new VirtualProgram();
+                    //vp->installDefaultColoringAndLightingShaders();
+                    //this->getOrCreateStateSet()->setAttributeAndModes( vp, 1 );
 
+                    // do we really need this? perhaps
                     node->addCullCallback( new UpdateLightingUniformsHelper() );
                 }
 
                 // attach to the transform:
-                getTransform()->addChild( node );
-                this->addChild( getTransform() );
+                _xform->addChild( node );
+
+                // insert a clamping agent if necessary:
+                replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) );
 
                 if ( sym->scale().isSet() )
                 {
@@ -129,7 +149,7 @@ ModelNode::init(const osgDB::Options* dbOptions)
                     this->setLocalRotation( rot.getRotate() );
                 }
 
-                applyStyle( *_style );
+                this->applyGeneralSymbology( _style );
             }
             else
             {
@@ -153,15 +173,18 @@ OSGEARTH_REGISTER_ANNOTATION( model, osgEarth::Annotation::ModelNode );
 
 
 ModelNode::ModelNode(MapNode* mapNode, const Config& conf, const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode, conf )
+LocalizedNode( mapNode, conf ),
+_dbOptions   ( dbOptions )
 {
+    _xform = new osg::MatrixTransform();
+
     conf.getObjIfSet( "style", _style );
 
     std::string uri = conf.value("url");
     if ( !uri.empty() )
-        _style->getOrCreate<ModelSymbol>()->url() = StringExpression(uri);
+        _style.getOrCreate<ModelSymbol>()->url() = StringExpression(uri);
 
-    init( dbOptions );
+    init();
 
     if ( conf.hasChild( "position" ) )
         setPosition( GeoPoint(conf.child("position")) );
@@ -172,8 +195,10 @@ ModelNode::getConfig() const
 {
     Config conf("model");
 
-    conf.updateObjIfSet( "style",    _style );
-    conf.updateObj     ( "position", getPosition() );
+    if ( !_style.empty() )
+        conf.addObj( "style", _style );
+
+    conf.addObj( "position", getPosition() );
 
     return conf;
 }
diff --git a/src/osgEarthAnnotation/OrthoNode b/src/osgEarthAnnotation/OrthoNode
index 3e5887b..1272a0c 100644
--- a/src/osgEarthAnnotation/OrthoNode
+++ b/src/osgEarthAnnotation/OrthoNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -33,8 +33,12 @@ namespace osgEarth { namespace Annotation
     /**
      * Base class for an annotation node that it drawn in screen-space
      * (as an orthographic overlay)
+     *
+     * Don't use this class directly. Use one of its subclasses instead like
+     * LabelNode or PlaceNode.
      */
-    class OSGEARTHANNO_EXPORT OrthoNode : public PositionedAnnotationNode, public SupportsDecluttering
+    class OSGEARTHANNO_EXPORT OrthoNode : public PositionedAnnotationNode, 
+                                          public SupportsDecluttering
     {
     public:
         META_AnnotationNode( osgEarthAnnotation, OrthoNode );
@@ -53,12 +57,6 @@ namespace osgEarth { namespace Annotation
             MapNode*        mapNode, 
             const GeoPoint& position );
 
-
-        /**
-         * dtor.
-         */
-        virtual ~OrthoNode() { }
-
         /**
          * Attaches a child node to the transforms. Use this instead of addChild.
          */
@@ -97,6 +95,10 @@ namespace osgEarth { namespace Annotation
         bool getOcclusionCulling() const;
         void setOcclusionCulling( bool value );
 
+    public: // AnnotationNode
+
+        virtual void applyStyle(const Style& style);
+
     public: // MapNodeObserver
 
         virtual void setMapNode( MapNode* mapNode );
@@ -107,6 +109,10 @@ namespace osgEarth { namespace Annotation
 
         virtual osg::BoundingSphere computeBound() const;
 
+    protected:
+        /** virtual dtor */
+        virtual ~OrthoNode() { }
+
     private:
         osg::Switch*                   _switch;
         osg::Group*                    _oq;
diff --git a/src/osgEarthAnnotation/OrthoNode.cpp b/src/osgEarthAnnotation/OrthoNode.cpp
index 75d62d3..04727bd 100644
--- a/src/osgEarthAnnotation/OrthoNode.cpp
+++ b/src/osgEarthAnnotation/OrthoNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -164,7 +164,7 @@ OrthoNode::traverse( osg::NodeVisitor& nv )
 
     if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
     {
-        cv = static_cast<osgUtil::CullVisitor*>( &nv );
+        cv = Culling::asCullVisitor(nv);
 
         // make sure that we're NOT using the AutoTransform if this node is in the decluttering bin;
         // the decluttering bin automatically manages screen space transformation.
@@ -277,6 +277,44 @@ OrthoNode::setPosition( const GeoPoint& position )
     return true;
 }
 
+void
+OrthoNode::applyStyle(const Style& style)
+{
+    // check for decluttering.
+    const TextSymbol* text = style.get<TextSymbol>();
+    if ( text && text->declutter().isSet() )
+    {
+        if ( text->declutter() == true )
+        {
+            this->getOrCreateStateSet()->setRenderBinDetails(
+                12,
+                OSGEARTH_DECLUTTER_BIN );
+        }
+        else
+        {
+            this->getOrCreateStateSet()->setRenderBinToInherit();
+        }
+    }
+
+    const IconSymbol* icon = style.get<IconSymbol>();
+    if ( icon && icon->declutter().isSet() )
+    {
+        if ( icon->declutter() == true )
+        {
+            this->getOrCreateStateSet()->setRenderBinDetails(
+                12,
+                OSGEARTH_DECLUTTER_BIN );
+        }
+        else
+        {
+            this->getOrCreateStateSet()->setRenderBinToInherit();
+        }
+    }
+
+    // up the chain
+    PositionedAnnotationNode::applyStyle( style );
+}
+
 bool
 OrthoNode::updateTransforms( const GeoPoint& p, osg::Node* patch )
 {
diff --git a/src/osgEarthAnnotation/PlaceNode b/src/osgEarthAnnotation/PlaceNode
index f651f77..5511be0 100644
--- a/src/osgEarthAnnotation/PlaceNode
+++ b/src/osgEarthAnnotation/PlaceNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -29,7 +29,7 @@ namespace osgEarth { namespace Annotation
 
     /** 
      * A PlaceNode combines an 2D icon, a label, support for mouse interaction
-     * and a popup control.
+     * and a popup control (eventually)
      */
     class OSGEARTHANNO_EXPORT PlaceNode : public OrthoNode
     {
@@ -38,6 +38,12 @@ namespace osgEarth { namespace Annotation
 
         /**
          * Constructs a new place node
+         *
+         * @param mapNode   MapNode that helps position this annotation
+         * @param position  Initial location of the annotation
+         * @param iconImage Image of the place icon
+         * @param labelText Text to place next to the icon
+         * @param style     Optional style settings.
          */
         PlaceNode(
             MapNode*           mapNode,
@@ -49,6 +55,11 @@ namespace osgEarth { namespace Annotation
         /**
          * Constructs a new place node. You can specify an icon marker by
          * adding a IconSymbol to the Style.
+         *
+         * @param mapNode   MapNode that helps position this annotation
+         * @param position  Initial location of the annotation
+         * @param labelText Text to place next to the icon
+         * @param style     Optional style settings.
          */
         PlaceNode(
             MapNode*           mapNode,
@@ -58,6 +69,11 @@ namespace osgEarth { namespace Annotation
 
         /**
          * Constuct a new place node entirely from symbology
+         *
+         * @param mapNode   MapNode that helps position this annotation
+         * @param position  Initial location of the annotation
+         * @param style     Style settings.
+         * @param dbOptions I/O options for embedded resource loading
          */
         PlaceNode(
             MapNode*              mapNode,
@@ -66,18 +82,17 @@ namespace osgEarth { namespace Annotation
             const osgDB::Options* dbOptions);
 
         /**
-         * Deserializes a place node
+         * Deserializes a place node from Config data. (internal method)
          */
         PlaceNode(
             MapNode*              mapNode,
             const Config&         conf,
             const osgDB::Options* dbOptions );
 
-        virtual ~PlaceNode() { }
-
         /**
          * Image to use for the icon
          */
+        void setIconImage(osg::Image* image);
         osg::Image* getIconImage() const { return _image.get(); }
 
         /**
@@ -87,8 +102,9 @@ namespace osgEarth { namespace Annotation
         const std::string& getText() const { return _text; }
 
         /**
-         * Style (for text)
+         * Style (for text and placement)
          */
+        void setStyle( const Style& style );
         const Style& getStyle() const { return _style; }
 
 
@@ -99,14 +115,19 @@ namespace osgEarth { namespace Annotation
         virtual void setDynamic( bool value );
 
         virtual Config getConfig() const;
+
+    protected:
+
+        virtual ~PlaceNode() { }
         
     private:
-        osg::ref_ptr<osg::Image>   _image;
-        std::string                _text;
-        Style                      _style;
-        class osg::Geode*          _geode;
+        osg::ref_ptr<osg::Image>           _image;
+        std::string                        _text;
+        Style                              _style;
+        class osg::Geode*                  _geode;
+        osg::ref_ptr<const osgDB::Options> _dbOptions;
 
-        void init(const osgDB::Options* options);
+        void init();
 
         // required by META_Node, but this object is not cloneable
         PlaceNode() { }
diff --git a/src/osgEarthAnnotation/PlaceNode.cpp b/src/osgEarthAnnotation/PlaceNode.cpp
index 84a9460..eac1f39 100644
--- a/src/osgEarthAnnotation/PlaceNode.cpp
+++ b/src/osgEarthAnnotation/PlaceNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -49,7 +49,7 @@ _text    ( text ),
 _style   ( style ),
 _geode   ( 0L )
 {
-    init( 0L );
+    init();
 }
 
 PlaceNode::PlaceNode(MapNode*           mapNode,
@@ -62,7 +62,7 @@ _text    ( text ),
 _style   ( style ),
 _geode   ( 0L )
 {
-    init( 0L );
+    init();
 }
 
 PlaceNode::PlaceNode(MapNode*              mapNode,
@@ -70,14 +70,19 @@ PlaceNode::PlaceNode(MapNode*              mapNode,
                      const Style&          style,
                      const osgDB::Options* dbOptions ) :
 OrthoNode ( mapNode, position ),
-_style    ( style )
+_style    ( style ),
+_dbOptions( dbOptions )
 {
-    init( dbOptions );
+    init();
 }
 
 void
-PlaceNode::init(const osgDB::Options* dbOptions)
+PlaceNode::init()
 {
+    //reset.
+    this->clearDecoration();
+    getAttachPoint()->removeChildren(0, getAttachPoint()->getNumChildren());
+
     _geode = new osg::Geode();
     osg::Drawable* text = 0L;
 
@@ -107,7 +112,7 @@ PlaceNode::init(const osgDB::Options* dbOptions)
 
         if ( !imageURI.empty() )
         {
-            _image = imageURI.getImage( dbOptions );
+            _image = imageURI.getImage( _dbOptions.get() );
         }
     }
 
@@ -197,6 +202,14 @@ PlaceNode::init(const osgDB::Options* dbOptions)
 
     ShaderGenerator gen( Registry::stateSetCache() );
     this->accept( gen );
+
+    // re-apply annotation drawable-level stuff as neccesary.
+    AnnotationData* ad = getAnnotationData();
+    if ( ad )
+        setAnnotationData( ad );
+
+    if ( _dynamic )
+        setDynamic( _dynamic );
 }
 
 
@@ -225,6 +238,24 @@ PlaceNode::setText( const std::string& text )
 
 
 void
+PlaceNode::setStyle(const Style& style)
+{
+    // changing the style requires a complete rebuild.
+    _style = style;
+    init();
+}
+
+
+void
+PlaceNode::setIconImage(osg::Image* image)
+{
+    // changing the icon requires a complete rebuild.
+    _image = image;
+    init();
+}
+
+
+void
 PlaceNode::setAnnotationData( AnnotationData* data )
 {
     OrthoNode::setAnnotationData( data );
@@ -260,7 +291,8 @@ OSGEARTH_REGISTER_ANNOTATION( place, osgEarth::Annotation::PlaceNode );
 PlaceNode::PlaceNode(MapNode*              mapNode,
                      const Config&         conf,
                      const osgDB::Options* dbOptions) :
-OrthoNode( mapNode, conf )
+OrthoNode ( mapNode, conf ),
+_dbOptions( dbOptions )
 {
     conf.getObjIfSet( "style",  _style );
     conf.getIfSet   ( "text",   _text );
@@ -274,7 +306,7 @@ OrthoNode( mapNode, conf )
             _image->setFileName( imageURI->base() );
     }
 
-    init( dbOptions );
+    init();
 
     if ( conf.hasChild("position") )
         setPosition( GeoPoint(conf.child("position")) );
diff --git a/src/osgEarthAnnotation/RectangleNode b/src/osgEarthAnnotation/RectangleNode
index 2202600..bd3f593 100644
--- a/src/osgEarthAnnotation/RectangleNode
+++ b/src/osgEarthAnnotation/RectangleNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -61,8 +61,7 @@ namespace osgEarth { namespace Annotation
             const GeoPoint&   position,
             const Linear&     width,
             const Linear&     height,
-            const Style&      style,
-            bool              draped      =true );
+            const Style&      style );
 
         virtual ~RectangleNode() { }
 
@@ -125,6 +124,9 @@ namespace osgEarth { namespace Annotation
         RectangleNode(MapNode* mapNode, const Config& conf, const osgDB::Options* options);
         virtual Config getConfig() const;
         
+    protected: // LocalizedNode
+
+        virtual osg::MatrixTransform* getTransform() { return _xform.get(); }
 
     private:
         RectangleNode() { }
@@ -132,10 +134,11 @@ namespace osgEarth { namespace Annotation
 
         void rebuild();
 
-        Style _style;
-        bool  _draped;        
+        Style  _style;
         Linear _width;
         Linear _height;
+
+        osg::ref_ptr<osg::MatrixTransform> _xform;
     };
 
 } } // namespace osgEarth::Annotation
diff --git a/src/osgEarthAnnotation/RectangleNode.cpp b/src/osgEarthAnnotation/RectangleNode.cpp
index 19ddc62..9872630 100644
--- a/src/osgEarthAnnotation/RectangleNode.cpp
+++ b/src/osgEarthAnnotation/RectangleNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -36,14 +36,13 @@ RectangleNode::RectangleNode(MapNode*          mapNode,
                              const GeoPoint&   position,
                              const Linear&     width,
                              const Linear&     height,
-                             const Style&      style,
-                             bool              draped ) :
-LocalizedNode( mapNode, position, false ),
+                             const Style&      style) :
+LocalizedNode( mapNode, position ),
 _width       ( width ),
 _height      ( height ),
-_style       ( style ),
-_draped      ( draped )
-{       
+_style       ( style )
+{
+    _xform = new osg::MatrixTransform();
     rebuild();
 }
 
@@ -96,7 +95,6 @@ RectangleNode::setStyle( const Style& style )
 }
 
 
-
 GeoPoint
 RectangleNode::getUpperLeft() const
 {
@@ -152,7 +150,7 @@ RectangleNode::getUpperRight() const
 void
 RectangleNode::setUpperRight( const GeoPoint& upperRight )
 {
-     GeoPoint center = getPosition();
+    GeoPoint center = getPosition();
 
     //Figure out the new width and height
     double earthRadius = center.getSRS()->getEllipsoid()->getRadiusEquator();
@@ -336,15 +334,10 @@ RectangleNode::rebuild()
     std::string currentDecoration = getDecoration();
     clearDecoration();
 
-    //Remove all children from this node
-    //removeChildren( 0, getNumChildren() );
-    if ( getRoot()->getNumParents() == 0 )
-    {
-        this->addChild( getRoot() );
-    }
-
-    //Remove all children from the attach point
-    getChildAttachPoint()->removeChildren( 0, getChildAttachPoint()->getNumChildren() );
+    // Reset:
+    osgEarth::clearChildren( this );
+    osgEarth::clearChildren( _xform.get() );
+    this->addChild( _xform.get() );
 
     // construct a local-origin circle.
     GeometryFactory factory;    
@@ -356,12 +349,11 @@ RectangleNode::rebuild()
         osg::Node* node = compiler.compile( feature.get(), _style, FilterContext(0L) );
         if ( node )
         {
-            getChildAttachPoint()->addChild( node );
-            getDrapeable()->setDraped( _draped );
+            _xform->addChild( node );
+            replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) );
         }
 
-        applyStyle( _style );
-
+        applyGeneralSymbology( _style );
         setLightingIfNotSet( false );
     }
 
@@ -378,13 +370,13 @@ OSGEARTH_REGISTER_ANNOTATION( rectangle, osgEarth::Annotation::RectangleNode );
 RectangleNode::RectangleNode(MapNode*              mapNode,
                              const Config&         conf,
                              const osgDB::Options* dbOptions) :
-LocalizedNode( mapNode, conf ),
-_draped      ( false )
+LocalizedNode( mapNode, conf )
 {
+    _xform = new osg::MatrixTransform();
+
     conf.getObjIfSet( "width", _width );
     conf.getObjIfSet( "height", _height );
     conf.getObjIfSet( "style",  _style );
-    conf.getIfSet   ( "draped", _draped );
 
     if ( conf.hasChild("position") )
         setPosition( GeoPoint(conf.child("position")) );
@@ -399,9 +391,6 @@ RectangleNode::getConfig() const
     conf.addObj( "width",  _width );
     conf.addObj( "height", _height );
     conf.addObj( "style",  _style );
-    if ( _draped != false )
-        conf.add( "draped", _draped );
-
     conf.addObj( "position", getPosition() );
 
     return conf;
diff --git a/src/osgEarthAnnotation/ScaleDecoration b/src/osgEarthAnnotation/ScaleDecoration
index a9f80b7..c814fbf 100644
--- a/src/osgEarthAnnotation/ScaleDecoration
+++ b/src/osgEarthAnnotation/ScaleDecoration
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthAnnotation/TrackNode b/src/osgEarthAnnotation/TrackNode
index bd683db..0b16e9a 100644
--- a/src/osgEarthAnnotation/TrackNode
+++ b/src/osgEarthAnnotation/TrackNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -20,7 +20,7 @@
 #define OSGEARTH_ANNOTATION_TRACK_NODE_H 1
 
 #include <osgEarthAnnotation/OrthoNode>
-#include <osgEarthSymbology/TextSymbol>
+#include <osgEarthSymbology/Style>
 #include <osgEarth/Containers>
 #include <osg/Image>
 #include <osgText/String>
@@ -98,17 +98,15 @@ namespace osgEarth { namespace Annotation
         /**
          * Constructs a new track node
          * @param mapNode     Map node under which this track will live
-         * @param position    Initial position (in map coordinates)
-         * @param image       Icon image to use
+         * @param position    Initial position
+         * @param style       Style containing an IconSymbol for the image
          * @param fieldSchema Schema for track label fields
          */
-        //TrackNode(
-        //    MapNode*                    mapNode,
-        //    const osg::Vec3d&           positionInMapCoords,
-        //    osg::Image*                 image,
-        //    const TrackNodeFieldSchema& fieldSchema );
-
-        virtual ~TrackNode() { }
+        TrackNode(
+            MapNode*                    mapNode,
+            const GeoPoint&             position,
+            const Style&                style,
+            const TrackNodeFieldSchema& fieldSchema );
 
         /** 
          * Sets the value of one of the field labels.
@@ -139,8 +137,11 @@ namespace osgEarth { namespace Annotation
         virtual void setAnnotationData( AnnotationData* data );
 
     protected:
-        osg::ref_ptr<osg::Image> _image;
-        class osg::Geode*        _geode;
+
+        virtual ~TrackNode() { }
+
+        Style             _style;
+        class osg::Geode* _geode;
 
         typedef fast_map<std::string, osg::Drawable*> NamedDrawables;
         NamedDrawables _namedDrawables;
diff --git a/src/osgEarthAnnotation/TrackNode.cpp b/src/osgEarthAnnotation/TrackNode.cpp
index feac0ad..7eb6d11 100644
--- a/src/osgEarthAnnotation/TrackNode.cpp
+++ b/src/osgEarthAnnotation/TrackNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -38,8 +38,24 @@ TrackNode::TrackNode(MapNode*                    mapNode,
                      osg::Image*                 image,
                      const TrackNodeFieldSchema& fieldSchema ) :
 
+OrthoNode   ( mapNode, position )
+{
+    if ( image )
+    {
+        IconSymbol* icon = _style.getOrCreate<IconSymbol>();
+        icon->setImage( image );
+    }
+
+    init( fieldSchema );
+}
+
+TrackNode::TrackNode(MapNode*                    mapNode, 
+                     const GeoPoint&             position,
+                     const Style&                style,
+                     const TrackNodeFieldSchema& fieldSchema ) :
+
 OrthoNode   ( mapNode, position ),
-_image      ( image )
+_style      ( style )
 {
     init( fieldSchema );
 }
@@ -48,13 +64,18 @@ void
 TrackNode::init( const TrackNodeFieldSchema& schema )
 {
     _geode = new osg::Geode();
-    
-    if ( _image.valid() )
+
+    IconSymbol* icon = _style.get<IconSymbol>();
+    osg::Image* image = icon ? icon->getImage() : 0L;
+
+    if ( icon && image )
     {
         // apply the image icon.
         osg::Geometry* imageGeom = AnnotationUtils::createImageGeometry( 
-            _image.get(),             // image
-            osg::Vec2s(0,0) );        // offset
+            image,                    // image
+            osg::Vec2s(0,0),          // offset
+            0,                        // tex image unit
+            icon->heading()->eval() );
 
         if ( imageGeom )
         {
@@ -95,6 +116,8 @@ TrackNode::init( const TrackNodeFieldSchema& schema )
     osg::StateSet* stateSet = _geode->getOrCreateStateSet();
     stateSet->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), 1 );
 
+    applyStyle( _style );
+
     setLightingIfNotSet( false );
 
     getAttachPoint()->addChild( _geode );
diff --git a/src/osgEarthDrivers/CMakeLists.txt b/src/osgEarthDrivers/CMakeLists.txt
index 1081523..bdb270b 100644
--- a/src/osgEarthDrivers/CMakeLists.txt
+++ b/src/osgEarthDrivers/CMakeLists.txt
@@ -71,6 +71,8 @@ ENDIF(SQLITE3_FOUND)
 
 ADD_SUBDIRECTORY(engine_osgterrain)
 ADD_SUBDIRECTORY(engine_quadtree)
+ADD_SUBDIRECTORY(engine_mp)
+ADD_SUBDIRECTORY(engine_byo)
 
 IF(V8_FOUND)
   ADD_SUBDIRECTORY(script_engine_v8)
diff --git a/src/osgEarthDrivers/agglite/AGGLiteOptions b/src/osgEarthDrivers/agglite/AGGLiteOptions
index 72ed958..0b50da8 100644
--- a/src/osgEarthDrivers/agglite/AGGLiteOptions
+++ b/src/osgEarthDrivers/agglite/AGGLiteOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -30,14 +30,6 @@ namespace osgEarth { namespace Drivers
     class AGGLiteOptions : public FeatureTileSourceOptions // NO EXPORT; header only
     {
     public:
-        /**
-         * If true, the rasterizer will adjust the line width based on the tile extent, having
-         * the effect of making lines appear approximately the same width at all scales.
-         * (Default = true)
-         */
-        optional<bool>& relativeLineSize() { return _relativeLineSize; }
-        const optional<bool>& relativeLineSize() const { return _relativeLineSize; }
-
         /** 
          * Whether to downsample line features to that they are no higher resolution than
          * the target image resolution. Defaults to true, but you can disable this (for a possible
@@ -50,8 +42,7 @@ namespace osgEarth { namespace Drivers
     public:
         AGGLiteOptions( const TileSourceOptions& options =TileSourceOptions() )
             : FeatureTileSourceOptions( options ),
-              _relativeLineSize(true), 
-              _optimizeLineSampling(true)
+              _optimizeLineSampling   ( true )
         {
             setDriver( "agglite" );
             fromConfig( _conf );
@@ -63,7 +54,6 @@ namespace osgEarth { namespace Drivers
     public:
         Config getConfig() const {
             Config conf = FeatureTileSourceOptions::getConfig();
-            conf.updateIfSet("relative_line_size", _relativeLineSize);
             conf.updateIfSet("optimize_line_sampling", _optimizeLineSampling);
             return conf;
         }
@@ -76,11 +66,9 @@ namespace osgEarth { namespace Drivers
 
     private:
         void fromConfig( const Config& conf ) {
-            conf.getIfSet( "relative_line_size", _relativeLineSize );
             conf.getIfSet( "optimize_line_sampling", _optimizeLineSampling );
         }
 
-        optional<bool> _relativeLineSize;
         optional<bool> _optimizeLineSampling;
     };
 
diff --git a/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp b/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp
index e0f9eec..6aab362 100644
--- a/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp
+++ b/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -22,7 +22,7 @@
 #include <osgEarthFeatures/TransformFilter>
 #include <osgEarthFeatures/BufferFilter>
 #include <osgEarthSymbology/Style>
-//TODO: replace this with ImageRasterizer
+//TODO: replace this with GeometryRasterizer
 #include <osgEarthSymbology/AGG.h>
 #include <osgEarth/Registry>
 #include <osgEarth/FileUtils>
@@ -35,7 +35,6 @@
 #include <osgDB/WriteFile>
 
 #include "AGGLiteOptions"
-//#include "agg.h"
 
 #include <sstream>
 #include <OpenThreads/Mutex>
@@ -77,7 +76,6 @@ public:
         agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 );
         agg::renderer<agg::span_abgr32> ren(rbuf);
         ren.clear(agg::rgba8(0,0,0,0));
-        //ren.clear(agg::rgba8(255,255,255,0));
         return true;
     }
 
@@ -115,8 +113,6 @@ public:
         // initialize:
         double xmin = imageExtent.xMin();
         double ymin = imageExtent.yMin();
-        //double s = (double)image->s();
-        //double t = (double)image->t();
         double xf = (double)image->s() / imageExtent.width();
         double yf = (double)image->t() / imageExtent.height();
 
@@ -172,7 +168,8 @@ public:
         if ( linesToBuffer.size() > 0 )
         {
             //We are buffering in the features native extent, so we need to use the transform extent to get the proper "resolution" for the image
-            GeoExtent transformedExtent = imageExtent.transform(context.profile()->getSRS());
+            const SpatialReference* featureSRS = context.profile()->getSRS();
+            GeoExtent transformedExtent = imageExtent.transform(featureSRS);
 
             double trans_xf = (double)image->s() / transformedExtent.width();
             double trans_yf = (double)image->t() / transformedExtent.height();
@@ -193,26 +190,64 @@ public:
 
             // now run the buffer operation on all lines:
             BufferFilter buffer;
-            float lineWidth = 0.5;
+            double lineWidth = 1.0;
             if ( masterLine )
             {
                 buffer.capStyle() = masterLine->stroke()->lineCap().value();
 
                 if ( masterLine->stroke()->width().isSet() )
+                {
                     lineWidth = masterLine->stroke()->width().value();
-            }
 
-            // "relative line size" means that the line width is expressed in (approx) pixels
-            // rather than in map units
-            if ( _options.relativeLineSize() == true )
-                buffer.distance() = xres * lineWidth;
-            else
-                buffer.distance() = lineWidth;
+                    GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS);
+                    double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s();
+
+                    // if the width units are specified, process them:
+                    if (masterLine->stroke()->widthUnits().isSet() &&
+                        masterLine->stroke()->widthUnits().get() != Units::PIXELS)
+                    {
+                        const Units& featureUnits = featureSRS->getUnits();
+                        const Units& strokeUnits  = masterLine->stroke()->widthUnits().value();
+
+                        // if the units are different than those of the feature data, we need to
+                        // do a units conversion.
+                        if ( featureUnits != strokeUnits )
+                        {
+                            if ( Units::canConvert(strokeUnits, featureUnits) )
+                            {
+                                // linear to linear, no problem
+                                lineWidth = strokeUnits.convertTo( featureUnits, lineWidth );
+                            }
+                            else if ( strokeUnits.isLinear() && featureUnits.isAngular() )
+                            {
+                                // linear to angular? approximate degrees per meter at the 
+                                // latitude of the tile's centroid.
+                                lineWidth = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth);
+                                double circ = featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI;
+                                double x, y;
+                                context.profile()->getExtent().getCentroid(x, y);
+                                double radians = (lineWidth/circ) * cos(osg::DegreesToRadians(y));
+                                lineWidth = osg::RadiansToDegrees(radians);
+                            }
+                        }
+
+                        // enfore a minimum width of one pixel.
+                        float minPixels = masterLine->stroke()->minPixels().getOrUse( 1.0f );
+                        lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels);
+                    }
+
+                    else // pixels
+                    {
+                        lineWidth *= pixelWidth;
+                    }
+                }
+            }
 
+            buffer.distance() = lineWidth * 0.5;   // since the distance is for one side
             buffer.push( linesToBuffer, context );
         }
 
-        // First, transform the features into the map's SRS:
+        // Transform the features into the map's SRS:
         TransformFilter xform( imageExtent.getSRS() );
         xform.setLocalizeCoordinates( false );
         context = xform.push( features, context );
@@ -249,7 +284,6 @@ public:
         for(FeatureList::iterator i = features.begin(); i != features.end(); i++)
         {
             Feature* feature = i->get();
-            //bool first = bd->_pass == 0 && i == features.begin();
 
             Geometry* geometry = feature->getGeometry();
 
@@ -300,10 +334,6 @@ public:
                     double x0 = xf*(p0.x()-xmin);
                     double y0 = yf*(p0.y()-ymin);
 
-                    //const osg::Vec3d& p1 = p+1 != g->end()? *(p+1) : g->front();
-                    //double x1 = xf*(p1.x()-xmin);
-                    //double y1 = yf*(p1.y()-ymin);
-
                     if ( p == g->begin() )
                         ras.move_to_d( x0, y0 );
                     else
@@ -315,19 +345,19 @@ public:
         }
 
         bd->_pass++;
-        return true;            
+        return true;
     }
 
     //override
     bool postProcess( osg::Image* image, osg::Referenced* data )
     {
-		//convert from ABGR to RGBA
-		unsigned char* pixel = image->data();
-		for(int i=0; i<image->s()*image->t()*4; i+=4, pixel+=4)
-		{
-			std::swap( pixel[0], pixel[3] );
-			std::swap( pixel[1], pixel[2] );
-		}
+        //convert from ABGR to RGBA
+        unsigned char* pixel = image->data();
+        for(int i=0; i<image->s()*image->t()*4; i+=4, pixel+=4)
+        {
+            std::swap( pixel[0], pixel[3] );
+            std::swap( pixel[1], pixel[2] );
+        }
         return true;
     }
 
diff --git a/src/osgEarthDrivers/arcgis/ArcGISOptions b/src/osgEarthDrivers/arcgis/ArcGISOptions
index 4e9de8f..fe27a64 100644
--- a/src/osgEarthDrivers/arcgis/ArcGISOptions
+++ b/src/osgEarthDrivers/arcgis/ArcGISOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/arcgis/Extent.h b/src/osgEarthDrivers/arcgis/Extent.h
index 2fb2a25..a162717 100644
--- a/src/osgEarthDrivers/arcgis/Extent.h
+++ b/src/osgEarthDrivers/arcgis/Extent.h
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/arcgis/MapService.h b/src/osgEarthDrivers/arcgis/MapService.h
index b097baa..002053d 100644
--- a/src/osgEarthDrivers/arcgis/MapService.h
+++ b/src/osgEarthDrivers/arcgis/MapService.h
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp b/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp
index 6933231..fe81566 100644
--- a/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp
+++ b/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/arcgis_map_cache/ReaderWriterArcGISMapCache.cpp b/src/osgEarthDrivers/arcgis_map_cache/ReaderWriterArcGISMapCache.cpp
index 269fc8f..d358cf8 100644
--- a/src/osgEarthDrivers/arcgis_map_cache/ReaderWriterArcGISMapCache.cpp
+++ b/src/osgEarthDrivers/arcgis_map_cache/ReaderWriterArcGISMapCache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/cache_filesystem/FileSystemCache b/src/osgEarthDrivers/cache_filesystem/FileSystemCache
index a6abf07..7e6fa3d 100644
--- a/src/osgEarthDrivers/cache_filesystem/FileSystemCache
+++ b/src/osgEarthDrivers/cache_filesystem/FileSystemCache
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/cache_filesystem/FileSystemCache.cpp b/src/osgEarthDrivers/cache_filesystem/FileSystemCache.cpp
index 00e61f3..a80d4aa 100644
--- a/src/osgEarthDrivers/cache_filesystem/FileSystemCache.cpp
+++ b/src/osgEarthDrivers/cache_filesystem/FileSystemCache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/debug/DebugOptions b/src/osgEarthDrivers/debug/DebugOptions
index b4aa106..81772ed 100644
--- a/src/osgEarthDrivers/debug/DebugOptions
+++ b/src/osgEarthDrivers/debug/DebugOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/debug/DebugTileSource.cpp b/src/osgEarthDrivers/debug/DebugTileSource.cpp
index 5dbe0e2..9ff19c4 100644
--- a/src/osgEarthDrivers/debug/DebugTileSource.cpp
+++ b/src/osgEarthDrivers/debug/DebugTileSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/earth/EarthFileSerializer b/src/osgEarthDrivers/earth/EarthFileSerializer
index 749b5c2..b2d8006 100644
--- a/src/osgEarthDrivers/earth/EarthFileSerializer
+++ b/src/osgEarthDrivers/earth/EarthFileSerializer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp b/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp
index d69f68b..b550cac 100644
--- a/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp
+++ b/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp b/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp
index 3b6cefc..f411446 100644
--- a/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp
+++ b/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp b/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp
index dc15657..7271d29 100644
--- a/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp
+++ b/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -175,18 +175,21 @@ class ReaderWriterEarth : public osgDB::ReaderWriter
             if ( !conf.empty() )
             {
                 // see if we were given a reference URI to use:
-                std::string refURI = uriContext.referrer();                                
+                std::string refURI = uriContext.referrer();
 
-                if ( conf.value("version") == "2" )
+                if ( conf.value("version") == "1" )
                 {
-                    OE_INFO << LC << "Detected a version 2.x earth file" << std::endl;
-                    EarthFileSerializer2 ser;
+                    OE_INFO << LC << "Detected a version 1.x earth file" << std::endl;
+                    EarthFileSerializer1 ser;
                     mapNode = ser.deserialize( conf, refURI );
                 }
+
                 else
                 {
-                    OE_INFO << LC << "Detected a version 1.x earth file" << std::endl;
-                    EarthFileSerializer1 ser;
+                    if ( conf.value("version") != "2" )
+                        OE_INFO << LC << "No valid earth file version; assuming version='2'" << std::endl;
+
+                    EarthFileSerializer2 ser;
                     mapNode = ser.deserialize( conf, refURI );
                 }
             }
diff --git a/src/osgEarthDrivers/engine_byo/BYOTerrainEngineDriver.cpp b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineDriver.cpp
new file mode 100644
index 0000000..fc0dfd4
--- /dev/null
+++ b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineDriver.cpp
@@ -0,0 +1,66 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 "BYOTerrainEngineNode"
+#include "BYOTerrainEngineOptions"
+#include <osgDB/FileNameUtils>
+
+#define LC "[engine_byo driver] "
+
+using namespace osgEarth::Drivers;
+using namespace osgEarth_engine_byo;
+
+/**
+ * osgEarth driver for the "Bring Your Own" terrain engine.
+ */
+class osgEarth_BYOTerrainEngineDriver : public osgDB::ReaderWriter
+{
+public:
+    osgEarth_BYOTerrainEngineDriver() {}
+
+    virtual const char* className()
+    {
+        return "osgEarth BYO Terrain Engine";
+    }
+
+    virtual bool acceptsExtension(const std::string& extension) const
+    {
+        return
+            osgDB::equalCaseInsensitive( extension, "osgearth_engine_byo" );
+    }
+
+    virtual ReadResult readObject(const std::string& uri, const Options* options) const
+    {
+        if ( "osgearth_engine_byo" == osgDB::getFileExtension( uri ) )
+        {
+            OE_INFO << LC << "Activated!" << std::endl;
+            return ReadResult( new BYOTerrainEngineNode() );
+        }
+        else
+        {
+            return ReadResult::FILE_NOT_HANDLED;
+        }
+    }    
+
+    virtual ReadResult readNode(const std::string& uri, const Options* options) const
+    {
+        return readObject(uri, options);
+    }
+};
+
+REGISTER_OSGPLUGIN(osgearth_engine_byo, osgEarth_BYOTerrainEngineDriver)
diff --git a/src/osgEarthDrivers/engine_byo/BYOTerrainEngineNode b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineNode
new file mode 100644
index 0000000..4a798eb
--- /dev/null
+++ b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineNode
@@ -0,0 +1,53 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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_BYO_ENGINE_NODE_H
+#define OSGEARTH_ENGINE_BYO_ENGINE_NODE_H 1
+
+#include <osgEarth/TerrainEngineNode>
+#include "BYOTerrainEngineOptions"
+
+using namespace osgEarth;
+
+namespace osgEarth_engine_byo
+{
+    class BYOTerrainEngineNode : public TerrainEngineNode
+    {
+    public:
+        BYOTerrainEngineNode();
+        META_Node(osgEarth, BYOTerrainEngineNode);
+        virtual ~BYOTerrainEngineNode();
+
+    public: // TerrainEngineNode
+
+        // for standalone tile creation outside of a terrain
+        osg::Node* createTile(const TileKey& key) { return 0L; }
+
+    public: // internal TerrainEngineNode
+
+        virtual void preInitialize( const Map* map, const TerrainOptions& options );
+        virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
+
+    private:
+        osgEarth::Drivers::BYOTerrainEngineOptions _terrainOptions;
+        BYOTerrainEngineNode( const BYOTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
+    };
+
+} // namespace osgEarth_engine_byo
+
+#endif // OSGEARTH_ENGINE_BYO_ENGINE_NODE_H
diff --git a/src/osgEarthDrivers/engine_byo/BYOTerrainEngineNode.cpp b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineNode.cpp
new file mode 100644
index 0000000..e24f6bc
--- /dev/null
+++ b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineNode.cpp
@@ -0,0 +1,78 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 "BYOTerrainEngineNode"
+#include "BYOTerrainEngineOptions"
+#include <osgEarth/ShaderGenerator>
+#include <osgEarth/Registry>
+
+#define LC "[BYOTerrainEngineNode] "
+
+using namespace osgEarth_engine_byo;
+using namespace osgEarth;
+using namespace osgEarth::Drivers;
+
+//------------------------------------------------------------------------
+
+BYOTerrainEngineNode::BYOTerrainEngineNode() :
+TerrainEngineNode( )
+{
+    //nop
+}
+
+BYOTerrainEngineNode::~BYOTerrainEngineNode()
+{
+    //nop
+}
+
+void
+BYOTerrainEngineNode::preInitialize( const Map* map, const TerrainOptions& options )
+{
+    TerrainEngineNode::preInitialize( map, options );
+    BYOTerrainEngineOptions myoptions(options);
+    if ( myoptions.getNode() )
+    {
+        this->addChild( myoptions.getNode() );
+    }
+    else if ( myoptions.url().isSet() )
+    {
+        OE_INFO << LC << "Loading terrain from " << myoptions.url()->full() << std::endl;
+
+        // no caching for this terrain.
+        osg::ref_ptr<osgDB::Options> dbOptions = Registry::instance()->cloneOrCreateOptions();
+        CachePolicy::NO_CACHE.apply( dbOptions.get() );
+
+        osg::Node* node = myoptions.url()->getNode( dbOptions.get() );
+        if ( node )
+        {
+            if ( myoptions.shaderPolicy() == SHADERPOLICY_GENERATE )
+            {
+                ShaderGenerator gen( Registry::stateSetCache() );
+                node->accept( gen );
+            }
+            else if ( myoptions.shaderPolicy() == SHADERPOLICY_DISABLE )
+            {
+                node->getOrCreateStateSet()->setAttributeAndModes(
+                    new osg::Program(),
+                    osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
+            }
+
+            this->addChild( node );
+        }
+    }
+}
diff --git a/src/osgEarthDrivers/engine_byo/BYOTerrainEngineOptions b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineOptions
new file mode 100644
index 0000000..0f036bb
--- /dev/null
+++ b/src/osgEarthDrivers/engine_byo/BYOTerrainEngineOptions
@@ -0,0 +1,91 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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_BYO_OPTIONS
+#define OSGEARTH_ENGINE_BYO_OPTIONS 1
+
+#include <osgEarth/Common>
+#include <osgEarth/ShaderUtils>
+#include <osgEarth/TerrainOptions>
+#include <osgEarth/URI>
+
+namespace osgEarth { namespace Drivers
+{
+    using namespace osgEarth;
+
+    /**
+     * Options for configuring the Bring Your Own Engine driver.
+     */
+    class BYOTerrainEngineOptions : public TerrainOptions // NO EXPORT (header-only)
+    {
+    public:
+        BYOTerrainEngineOptions( const ConfigOptions& options =ConfigOptions() ) 
+            : TerrainOptions( options ),
+              _shaderPolicy ( SHADERPOLICY_GENERATE )
+        {
+            setDriver( "byo" );
+            fromConfig( _conf );
+        }
+
+        /** dtor */
+        virtual ~BYOTerrainEngineOptions() { }
+
+    public:
+        optional<URI>& url() { return _uri; }
+        const optional<URI>& url() const { return _uri; }
+
+        optional<ShaderPolicy>& shaderPolicy() { return _shaderPolicy; }
+        const optional<ShaderPolicy>& shaderPolicy() const { return _shaderPolicy; }
+
+    public: // non-serializable
+        void setNode(osg::Node* node);
+        osg::Node* getNode() { return _node.get(); }
+
+    protected:
+        virtual Config getConfig() const {
+            Config conf = TerrainOptions::getConfig();
+            conf.updateIfSet( "url", _uri );
+            conf.updateIfSet( "shader_policy", "disable",  _shaderPolicy, SHADERPOLICY_DISABLE );
+            conf.updateIfSet( "shader_policy", "inherit",  _shaderPolicy, SHADERPOLICY_INHERIT );
+            conf.updateIfSet( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE );
+            conf.updateNonSerializable( "node", _node.get() );
+            return conf;
+        }
+
+        virtual void mergeConfig( const Config& conf ) {
+            TerrainOptions::mergeConfig( conf );
+            fromConfig( conf );
+        }
+
+    private:
+        void fromConfig( const Config& conf ) {
+            conf.getIfSet( "url", _uri );
+            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>( "node" );
+        }
+
+        optional<URI>           _uri;
+        optional<ShaderPolicy>  _shaderPolicy;
+        osg::ref_ptr<osg::Node> _node;
+    };
+
+} } // namespace osgEarth::Drivers
+
+#endif // OSGEARTH_ENGINE_MP_OPTIONS
diff --git a/src/osgEarthDrivers/engine_byo/CMakeLists.txt b/src/osgEarthDrivers/engine_byo/CMakeLists.txt
new file mode 100644
index 0000000..1af02a3
--- /dev/null
+++ b/src/osgEarthDrivers/engine_byo/CMakeLists.txt
@@ -0,0 +1,20 @@
+
+SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthSymbology)
+
+SET(TARGET_SRC
+    BYOTerrainEngineNode.cpp
+    BYOTerrainEngineDriver.cpp
+)
+
+SET(TARGET_H
+    Common
+    BYOTerrainEngineNode
+    BYOTerrainEngineOptions
+)
+
+SETUP_PLUGIN(osgearth_engine_byo)
+
+# to install public driver includes:
+SET(LIB_NAME engine_byo)
+SET(LIB_PUBLIC_HEADERS ${TARGET_H})
+INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL)
diff --git a/src/osgEarthDrivers/engine_quadtree/Common b/src/osgEarthDrivers/engine_byo/Common
similarity index 82%
copy from src/osgEarthDrivers/engine_quadtree/Common
copy to src/osgEarthDrivers/engine_byo/Common
index 3b84dd8..0177e43 100644
--- a/src/osgEarthDrivers/engine_quadtree/Common
+++ b/src/osgEarthDrivers/engine_byo/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,9 +16,9 @@
  * 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_COMMON_H
-#define OSGEARTH_ENGINE_QUADTREE_COMMON_H 1
+#ifndef OSGEARTH_ENGINE_BYO_COMMON_H
+#define OSGEARTH_ENGINE_BYO_COMMON_H 1
 
 #include <osgEarth/Common>
 
-#endif // OSGEARTH_ENGINE_QUADTREE_COMMON_H
+#endif // OSGEARTH_ENGINE_BYO_COMMON_H
diff --git a/src/osgEarthDrivers/engine_mp/CMakeLists.txt b/src/osgEarthDrivers/engine_mp/CMakeLists.txt
new file mode 100644
index 0000000..1e3680b
--- /dev/null
+++ b/src/osgEarthDrivers/engine_mp/CMakeLists.txt
@@ -0,0 +1,45 @@
+
+SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthSymbology)
+
+SET(TARGET_SRC
+    CustomPagedLOD.cpp
+    KeyNodeFactory.cpp
+    LODFactorCallback.cpp
+    MPGeometry.cpp
+    MPTerrainEngineNode.cpp
+    MPTerrainEngineDriver.cpp
+    SerialKeyNodeFactory.cpp
+    TerrainNode.cpp
+    TileModelCompiler.cpp
+    TileNode.cpp
+    TileNodeRegistry.cpp
+    TileModelFactory.cpp
+)
+
+SET(TARGET_H
+    Common
+    CustomPagedLOD
+    DynamicLODScaleCallback
+    FileLocationCallback
+    KeyNodeFactory
+    LODFactorCallback
+    MPGeometry
+    MPTerrainEngineNode
+    MPTerrainEngineOptions
+    QuickReleaseGLObjects
+    SerialKeyNodeFactory
+    TerrainNode
+    TileModel
+    TileModelCompiler
+    TileNode
+    TileNodeRegistry
+    TileModelFactory
+)
+
+SETUP_PLUGIN(osgearth_engine_mp)
+
+# to install public driver includes:
+SET(LIB_NAME engine_mp)
+SET(LIB_PUBLIC_HEADERS ${TARGET_H})
+INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL)
+
diff --git a/src/osgEarthDrivers/engine_quadtree/Common b/src/osgEarthDrivers/engine_mp/Common
similarity index 82%
copy from src/osgEarthDrivers/engine_quadtree/Common
copy to src/osgEarthDrivers/engine_mp/Common
index 3b84dd8..ebe553f 100644
--- a/src/osgEarthDrivers/engine_quadtree/Common
+++ b/src/osgEarthDrivers/engine_mp/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,9 +16,9 @@
  * 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_COMMON_H
-#define OSGEARTH_ENGINE_QUADTREE_COMMON_H 1
+#ifndef OSGEARTH_ENGINE_MP_COMMON_H
+#define OSGEARTH_ENGINE_MP_COMMON_H 1
 
 #include <osgEarth/Common>
 
-#endif // OSGEARTH_ENGINE_QUADTREE_COMMON_H
+#endif // OSGEARTH_ENGINE_MP_COMMON_H
diff --git a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD b/src/osgEarthDrivers/engine_mp/CustomPagedLOD
similarity index 89%
copy from src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
copy to src/osgEarthDrivers/engine_mp/CustomPagedLOD
index 477f051..df31593 100644
--- a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
+++ b/src/osgEarthDrivers/engine_mp/CustomPagedLOD
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -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_ENGINE_QUADTREE_CUSTOM_PAGED_LOD
-#define OSGEARTH_ENGINE_QUADTREE_CUSTOM_PAGED_LOD 1
+#ifndef OSGEARTH_ENGINE_MP_CUSTOM_PAGED_LOD
+#define OSGEARTH_ENGINE_MP_CUSTOM_PAGED_LOD 1
 
 #include "Common"
 #include "TileNode"
@@ -26,7 +26,7 @@
 
 using namespace osgEarth;
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     /**
      * Customized PagedLOD node that automatically registers TileNodes
@@ -68,6 +68,6 @@ namespace osgEarth_engine_quadtree
         osg::ref_ptr<TileNodeRegistry> _live, _dead;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_CUSTOM_PAGED_LOD
+#endif // OSGEARTH_ENGINE_MP_CUSTOM_PAGED_LOD
diff --git a/src/osgEarthDrivers/engine_mp/CustomPagedLOD.cpp b/src/osgEarthDrivers/engine_mp/CustomPagedLOD.cpp
new file mode 100644
index 0000000..282abac
--- /dev/null
+++ b/src/osgEarthDrivers/engine_mp/CustomPagedLOD.cpp
@@ -0,0 +1,111 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 "CustomPagedLOD"
+
+using namespace osgEarth_engine_mp;
+using namespace osgEarth;
+
+#define LC "[CustomPagedLOD] "
+
+
+//----------------------------------------------------------------------------
+
+CustomPagedLOD::CustomPagedLOD(TileNodeRegistry* live,
+                               TileNodeRegistry* dead) :
+_live( live ),
+_dead( dead )
+{
+    //nop
+}
+
+
+CustomPagedLOD::~CustomPagedLOD()
+{
+    if ( _live.valid() || _dead.valid() )
+    {
+        for( unsigned i=0; i < getNumChildren(); ++i )
+        {
+            osg::ref_ptr<TileNode> node = dynamic_cast<TileNode*>( getChild(i) );
+            if ( node.valid() )
+            {
+                if ( _live.valid() )
+                    _live->remove( node.get() );
+                if ( _dead.valid() )
+                    _dead->add( node.get() );
+            }
+        }
+    }
+}
+
+
+bool
+CustomPagedLOD::addChild( osg::Node* child )
+{
+    bool ok = osg::PagedLOD::addChild( child );
+    if ( ok && _live.valid() )
+    {
+        TileNodeGroup* tileGroup = dynamic_cast<TileNodeGroup*>( child );
+        if ( tileGroup )
+        {
+            TileNodeVector tileNodes;
+            tileNodes.reserve( 4 );
+
+            for( unsigned i=0; i<tileGroup->getNumChildren(); ++i )
+            {
+                osg::Node* subChild = tileGroup->getChild(i);
+                TileNode* tileNode = 0L;
+                osg::Group* group = dynamic_cast<osg::PagedLOD*>(subChild);
+                if ( group && group->getNumChildren() > 0 )
+                    tileNode = dynamic_cast<TileNode*>( group->getChild(0) );
+                else
+                    tileNode = dynamic_cast<TileNode*>( child );
+                if ( tileNode )
+                    tileNodes.push_back( tileNode );
+            }
+
+            if ( !tileNodes.empty() )
+                _live->add( tileNodes );
+        }
+    }
+    return ok;
+}
+
+
+bool 
+CustomPagedLOD::removeChildren(unsigned pos, unsigned numChildrenToRemove)
+{
+    if ( _live.valid() || _dead.valid() )
+    {
+        for( unsigned i=pos; i<pos+numChildrenToRemove; ++i )
+        {
+            if ( i < getNumChildren() )
+            {
+                osg::ref_ptr<TileNode> tile = dynamic_cast<TileNode*>( getChild(i) );
+                if ( tile.valid() )
+                {
+                    if ( _live.valid() )
+                        _live->remove( tile.get() );
+                    if ( _dead.valid() )
+                        _dead->add( tile.get() );
+                }
+            }
+        }
+    }
+    return osg::PagedLOD::removeChildren( pos, numChildrenToRemove );
+}
diff --git a/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback b/src/osgEarthDrivers/engine_mp/DynamicLODScaleCallback
similarity index 96%
copy from src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
copy to src/osgEarthDrivers/engine_mp/DynamicLODScaleCallback
index 0f8b31f..91b9125 100644
--- a/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
+++ b/src/osgEarthDrivers/engine_mp/DynamicLODScaleCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -23,7 +23,7 @@
 #include <osg/NodeCallback>
 #include <osg/CullStack>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     /**
      * Cull callback that dynamically computes an LOD scale based on
@@ -80,6 +80,6 @@ namespace osgEarth_engine_quadtree
         float _fallOff;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
 #endif //OSGEARTH_ENGINE_OSGTERRAIN_DYNAMIC_LOD_SCALE_CALLBACK_H
diff --git a/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback b/src/osgEarthDrivers/engine_mp/FileLocationCallback
similarity index 90%
copy from src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
copy to src/osgEarthDrivers/engine_mp/FileLocationCallback
index b826cb6..9400031 100644
--- a/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
+++ b/src/osgEarthDrivers/engine_mp/FileLocationCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -20,17 +20,18 @@
 #ifndef OSGEARTH_ENGINE_OSGTERRAIN_FILE_LOCATION_CALLBACK_H
 #define OSGEARTH_ENGINE_OSGTERRAIN_FILE_LOCATION_CALLBACK_H 1
 
-#include "OSGTerrainEngineNode"
+#include "MPTerrainEngineNode"
 #include <osg/Version>
 #include <osgEarth/Export>
 
 
-namespace osgEarth_engine_osgterrain
-{
 #define USE_FILELOCATIONCALLBACK OSG_MIN_VERSION_REQUIRED(2,9,5)
 
+
 #if USE_FILELOCATIONCALLBACK
 
+namespace osgEarth_engine_mp
+{
     /**
      * A database pager callback that determines if the given filename is cached or not
      */
@@ -50,8 +51,8 @@ namespace osgEarth_engine_osgterrain
             unsigned int lod, x, y, id;
             sscanf(filename.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &id);
 
-            osg::ref_ptr<OSGTerrainEngineNode> engine;
-            OSGTerrainEngineNode::getEngineByUID( (UID)id, engine );
+            osg::ref_ptr<MPTerrainEngineNode> engine;
+            MPTerrainEngineNode::getEngineByUID( (UID)id, engine );
 
             if ( engine.valid() )
             {
@@ -78,7 +79,7 @@ namespace osgEarth_engine_osgterrain
         virtual bool useFileCache() const { return false; }
     };
 
-} //namespace osgEarth_engine_osgterrain
+} // namespace osgEarth_engine_mp
 
 #endif // USE_FILELOCATIONCALLBACK
 
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory b/src/osgEarthDrivers/engine_mp/KeyNodeFactory
similarity index 93%
copy from src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
copy to src/osgEarthDrivers/engine_mp/KeyNodeFactory
index 85900fa..43a467f 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
+++ b/src/osgEarthDrivers/engine_mp/KeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@
 
 using namespace osgEarth;
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     /**
     * Factory object that can create a scene graph given a TileKey.
@@ -52,6 +52,6 @@ namespace osgEarth_engine_quadtree
         virtual ~KeyNodeFactory() { }
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
 #endif // OSGEARTH_ENGINE_KEY_NODE_FACTORY
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp b/src/osgEarthDrivers/engine_mp/KeyNodeFactory.cpp
similarity index 92%
copy from src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
copy to src/osgEarthDrivers/engine_mp/KeyNodeFactory.cpp
index fa94232..559d695 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_mp/KeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
 */
 #include "KeyNodeFactory"
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 
 #define LC "[KeyNodeFactory] "
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp b/src/osgEarthDrivers/engine_mp/LODFactorCallback
similarity index 66%
copy from src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
copy to src/osgEarthDrivers/engine_mp/LODFactorCallback
index fa94232..2c4a8c9 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_mp/LODFactorCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2011 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,16 +16,19 @@
 * 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 "KeyNodeFactory"
 
-using namespace osgEarth_engine_quadtree;
-using namespace osgEarth;
+#ifndef OSGEARTH_ENGINE_OSGTERRAIN_LOD_FACTOR_CALLBACK_H
+#define OSGEARTH_ENGINE_OSGTERRAIN_LOD_FACTOR_CALLBACK_H 1
 
-#define LC "[KeyNodeFactory] "
+#include <osg/NodeCallback>
 
-//--------------------------------------------------------------------------
-
-KeyNodeFactory::KeyNodeFactory()
+namespace osgEarth_engine_mp
 {
-    //NOP
-}
+    struct LODFactorCallback : public osg::NodeCallback
+    {
+        void operator()(osg::Node* node, osg::NodeVisitor* nv);
+    };
+
+} // namespace osgEarth_engine_mp
+
+#endif
diff --git a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp b/src/osgEarthDrivers/engine_mp/LODFactorCallback.cpp
similarity index 95%
copy from src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
copy to src/osgEarthDrivers/engine_mp/LODFactorCallback.cpp
index 04ad128..7303b43 100644
--- a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
+++ b/src/osgEarthDrivers/engine_mp/LODFactorCallback.cpp
@@ -18,13 +18,13 @@
  */
 #include "LODFactorCallback"
 
+#include <osgEarth/CullingUtils>
 #include <osg/Math>
 #include <osg/PagedLOD>
 #include <osg/StateSet>
 #include <osg/Uniform>
-#include <osgUtil/CullVisitor>
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 
 // This callback sets a uniform, osgearth_LODRangeFactor, based on the
 // distance from the camera and its relation to the minimum and
@@ -36,7 +36,7 @@ void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
     // 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);
+    osgUtil::CullVisitor* cv = osgEarth::Culling::asCullVisitor(nv);
     osg::LOD::RangeMode rangeMode = lod->getRangeMode();
     float requiredRange = 0.0f;
     float rangeFactor = 1.0f;
diff --git a/src/osgEarthDrivers/engine_mp/MPGeometry b/src/osgEarthDrivers/engine_mp/MPGeometry
new file mode 100644
index 0000000..53c8374
--- /dev/null
+++ b/src/osgEarthDrivers/engine_mp/MPGeometry
@@ -0,0 +1,101 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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_MP_CUSTOM_PAGED_LOD
+#define OSGEARTH_ENGINE_MP_CUSTOM_PAGED_LOD 1
+
+#include "Common"
+#include "TileNode"
+#include "TileNodeRegistry"
+#include <osg/Geometry>
+#include <osgEarth/Map>
+#include <osgEarth/MapFrame>
+
+
+using namespace osgEarth;
+
+namespace osgEarth_engine_mp
+{
+    /**
+     * A Geometry that will draw its primitive sets multiple time,
+     * once for each configured "layer". For rendering multipass
+     * data from a single cull.
+     */
+    class MPGeometry : public osg::Geometry
+    {
+    public:
+        /**
+         * Per-tile attribution for use with the MPGeometry.
+         */
+        struct Layer
+        {
+            osgEarth::UID                  _layerID;
+            osg::ref_ptr<const ImageLayer> _imageLayer;
+            osg::ref_ptr<osg::Texture>     _tex;
+            osg::ref_ptr<osg::Vec2Array>   _texCoords;
+            float                          _alphaThreshold;
+
+            // in support of std::find
+            inline bool operator == (const osgEarth::UID& rhs) const {
+                return _layerID == rhs;
+            }
+        };
+
+    public:
+        mutable MapFrame                     _map;
+        mutable std::vector<Layer>           _layers;
+        mutable Threading::Mutex             _mapSyncMutex;
+        mutable osg::ref_ptr<osg::Uniform>   _layerUIDUniform;
+        mutable osg::ref_ptr<osg::Uniform>   _layerOrderUniform;
+        mutable osg::ref_ptr<osg::Uniform>   _opacityUniform;
+        int                                  _textureImageUnit;
+
+    public:
+        
+        // construct a new MPGeometry.
+        MPGeometry(const Map* map, int textureImageUnit);
+
+        // render all passes of the geometry.
+        void renderPrimitiveSets(osg::State& state, bool usingVBOs) const;
+
+
+    public: // osg::Geometry overrides
+
+        // override so we can properly release the GL buffer objects
+        // that are not tracked by the Geometry itself but rather are
+        // stored in the LayerRenderData.
+        void releaseGLObjects(osg::State* state) const;
+        void compileGLObjects( osg::RenderInfo& renderInfo ) const;
+
+
+        // this is copied mostly from osg::Geometry, but we've removed 
+        // all the code that handles non-fastPath (don't need it) and
+        // called into our custom renderPrimitiveSets method.
+        void drawImplementation(osg::RenderInfo& renderInfo) const;
+        
+
+    public:
+        META_Object(osgEarth, MPGeometry);
+        MPGeometry() : osg::Geometry(), _map(0L) { }
+        MPGeometry(const MPGeometry& rhs, const osg::CopyOp& cop) : osg::Geometry(rhs, cop), _map(rhs._map) { }
+        virtual ~MPGeometry() { }
+    };
+
+} // namespace osgEarth_engine_mp
+
+#endif // OSGEARTH_ENGINE_MP_CUSTOM_PAGED_LOD
diff --git a/src/osgEarthDrivers/engine_mp/MPGeometry.cpp b/src/osgEarthDrivers/engine_mp/MPGeometry.cpp
new file mode 100644
index 0000000..a6a015b
--- /dev/null
+++ b/src/osgEarthDrivers/engine_mp/MPGeometry.cpp
@@ -0,0 +1,277 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 "MPGeometry"
+
+using namespace osgEarth_engine_mp;
+using namespace osgEarth;
+
+#define LC "[MPGeometry] "
+
+
+//----------------------------------------------------------------------------
+
+MPGeometry::MPGeometry(const Map* map, int textureImageUnit) : 
+osg::Geometry    ( ), 
+_map             (map, Map::IMAGE_LAYERS),
+_textureImageUnit( textureImageUnit )
+{
+    _opacityUniform = new osg::Uniform( osg::Uniform::FLOAT, "oe_layer_opacity" );
+    _opacityUniform->set( 1.0f );
+
+    _layerUIDUniform = new osg::Uniform( osg::Uniform::INT, "oe_layer_uid" );
+    _layerUIDUniform->set( 0 );
+
+    _layerOrderUniform = new osg::Uniform( osg::Uniform::INT, "oe_layer_order" );
+    _layerOrderUniform->set( 0 );
+}
+
+
+void
+MPGeometry::renderPrimitiveSets(osg::State& state,
+                                bool        usingVBOs) const
+{
+    // check the map frame to see if it's up to date
+    if ( _map.needsSync() )
+    {
+        // this lock protects a MapFrame sync when we have multiple DRAW threads.
+        Threading::ScopedMutexLock exclusive( _mapSyncMutex );
+
+        if ( _map.needsSync() && _map.sync() ) // always double check
+        {
+            // This should only happen is the layer ordering changes;
+            // If layers are added or removed, the Tile gets rebuilt and
+            // the point is moot.
+            std::vector<Layer> reordered;
+            const ImageLayerVector& layers = _map.imageLayers();
+            reordered.reserve( layers.size() );
+            for( ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
+            {
+                std::vector<Layer>::iterator j = std::find( _layers.begin(), _layers.end(), i->get()->getUID() );
+                if ( j != _layers.end() )
+                    reordered.push_back( *j );
+            }
+            _layers.swap( reordered );
+        }
+    }
+
+    unsigned layersDrawn = 0;
+
+
+    osg::ref_ptr<osg::GL2Extensions> ext = osg::GL2Extensions::Get( state.getContextID(), true );
+    const osg::Program::PerContextProgram* pcp = state.getLastAppliedProgramObject();
+
+    GLint opacityLocation;
+    GLint uidLocation;
+    GLint orderLocation;
+
+    // yes, it's possible that the PCP is not set up yet.
+    // TODO: can we optimize this so we don't need to get uni locations every time?
+    if ( pcp )
+    {
+        opacityLocation = pcp->getUniformLocation( _opacityUniform->getNameID() );
+        uidLocation     = pcp->getUniformLocation( _layerUIDUniform->getNameID() );
+        orderLocation   = pcp->getUniformLocation( _layerOrderUniform->getNameID() );
+    }
+
+    if ( _layers.size() > 0 )
+    {
+        float prev_opacity        = -1.0f;
+        float prev_alphaThreshold = -1.0f;
+
+        // activate the image unit.
+        state.setActiveTextureUnit( _textureImageUnit );
+
+        // interate over all the image layers
+        for(unsigned i=0; i<_layers.size(); ++i)
+        {
+            const Layer& layer = _layers[i];
+
+            if ( layer._imageLayer->getVisible() )
+            {
+                // bind the texture for this layer:
+                layer._tex->apply( state );
+
+                // bind the texture coordinates for this layer.
+                // TODO: can probably optimize this by sharing or using texture matrixes.
+                // State::setTexCoordPointer does some redundant work under the hood.
+                state.setTexCoordPointer( _textureImageUnit, layer._texCoords.get() );
+
+                // apply uniform values:
+                if ( pcp )
+                {
+                    // apply opacity:
+                    float opacity = layer._imageLayer->getOpacity();
+                    if ( opacity != prev_opacity )
+                    {
+                        _opacityUniform->set( opacity );
+                        _opacityUniform->apply( ext, opacityLocation );
+                        prev_opacity = opacity;
+                    }
+
+                    // assign the layer UID:
+                    _layerUIDUniform->set( layer._layerID );
+                    _layerUIDUniform->apply( ext, uidLocation );
+
+                    // assign the layer order:
+                    _layerOrderUniform->set( (int)layersDrawn );
+                    _layerOrderUniform->apply( ext, orderLocation );
+                }
+
+                // draw the primitive sets.
+                for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
+                {
+                    const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
+                    primitiveset->draw(state, usingVBOs);
+                }
+
+                ++layersDrawn;
+            }
+        }
+
+        // prevent texture leakage
+        glBindTexture( GL_TEXTURE_2D, 0 );
+    }
+
+    // if we didn't draw anything, draw the raw tiles anyway with no texture.
+    if ( layersDrawn == 0 )
+    {
+        _opacityUniform->set( 1.0f );
+        _opacityUniform->apply( ext, opacityLocation );
+
+        _layerUIDUniform->set( (int)-1 ); // indicates a non-textured layer
+        _layerUIDUniform->apply( ext, uidLocation );
+
+        _layerOrderUniform->set( (int)0 );
+        _layerOrderUniform->apply( ext, orderLocation );
+
+        // draw the primitives themselves.
+        for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
+        {
+            const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
+            primitiveset->draw(state, usingVBOs);
+        }
+    }
+}
+
+
+void 
+MPGeometry::releaseGLObjects(osg::State* state) const
+{
+    osg::Geometry::releaseGLObjects( state );
+
+    for(unsigned i=0; i<_layers.size(); ++i)
+    {
+        const Layer& layer = _layers[i];
+        if ( layer._tex.valid() )
+            layer._tex->releaseGLObjects( state );
+        if ( layer._texCoords.valid() )
+            layer._texCoords->releaseGLObjects( state );
+    }
+}
+
+
+void 
+MPGeometry::compileGLObjects( osg::RenderInfo& renderInfo ) const
+{
+    osg::Geometry::compileGLObjects( renderInfo );
+
+    for(unsigned i=0; i<_layers.size(); ++i)
+    {
+        const Layer& layer = _layers[i];
+        if ( layer._tex.valid() )
+            layer._tex->apply( *renderInfo.getState() );
+    }
+}
+
+
+void 
+MPGeometry::drawImplementation(osg::RenderInfo& renderInfo) const
+{
+    osg::State& state = *renderInfo.getState();
+
+    bool usingVertexBufferObjects = _useVertexBufferObjects && state.isVertexBufferObjectSupported();
+    bool handleVertexAttributes = !_vertexAttribList.empty();
+
+    osg::ArrayDispatchers& arrayDispatchers = state.getArrayDispatchers();
+
+    arrayDispatchers.reset();
+    arrayDispatchers.setUseVertexAttribAlias(state.getUseVertexAttributeAliasing());
+    arrayDispatchers.setUseGLBeginEndAdapter(false);
+
+    arrayDispatchers.activateNormalArray(_normalData.binding, _normalData.array.get(), _normalData.indices.get());
+    arrayDispatchers.activateColorArray(_colorData.binding, _colorData.array.get(), _colorData.indices.get());
+    arrayDispatchers.activateSecondaryColorArray(_secondaryColorData.binding, _secondaryColorData.array.get(), _secondaryColorData.indices.get());
+    arrayDispatchers.activateFogCoordArray(_fogCoordData.binding, _fogCoordData.array.get(), _fogCoordData.indices.get());
+
+    if (handleVertexAttributes)
+    {
+        for(unsigned int unit=0;unit<_vertexAttribList.size();++unit)
+        {
+            arrayDispatchers.activateVertexAttribArray(_vertexAttribList[unit].binding, unit, _vertexAttribList[unit].array.get(), _vertexAttribList[unit].indices.get());
+        }
+    }
+
+    // dispatch any attributes that are bound overall
+    arrayDispatchers.dispatch(BIND_OVERALL,0);
+    state.lazyDisablingOfVertexAttributes();
+
+    // set up arrays
+    if( _vertexData.array.valid() )
+        state.setVertexPointer(_vertexData.array.get());
+
+    if (_normalData.binding==BIND_PER_VERTEX && _normalData.array.valid())
+        state.setNormalPointer(_normalData.array.get());
+
+    if (_colorData.binding==BIND_PER_VERTEX && _colorData.array.valid())
+        state.setColorPointer(_colorData.array.get());
+
+    if (_secondaryColorData.binding==BIND_PER_VERTEX && _secondaryColorData.array.valid())
+        state.setSecondaryColorPointer(_secondaryColorData.array.get());
+
+    if (_fogCoordData.binding==BIND_PER_VERTEX && _fogCoordData.array.valid())
+        state.setFogCoordPointer(_fogCoordData.array.get());
+
+    for(unsigned int unit=0;unit<_texCoordList.size();++unit)
+    {
+        const osg::Array* array = _texCoordList[unit].array.get();
+        if (array) state.setTexCoordPointer(unit,array);
+    }
+
+    if( handleVertexAttributes )
+    {
+        for(unsigned int index = 0; index < _vertexAttribList.size(); ++index )
+        {
+            const osg::Array* array = _vertexAttribList[index].array.get();
+            const AttributeBinding ab = _vertexAttribList[index].binding;
+            if( ab == BIND_PER_VERTEX && array )
+            {
+                state.setVertexAttribPointer( index, array, _vertexAttribList[index].normalize );
+            }
+        }
+    }
+
+    state.applyDisablingOfVertexAttributes();
+
+    // draw the multipass geometry.
+    renderPrimitiveSets(state, usingVertexBufferObjects);
+
+    // unbind the VBO's if any are used.
+    state.unbindVertexBufferObject();
+    state.unbindElementBufferObject();
+}
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp b/src/osgEarthDrivers/engine_mp/MPTerrainEngineDriver.cpp
similarity index 83%
copy from src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
copy to src/osgEarthDrivers/engine_mp/MPTerrainEngineDriver.cpp
index a4143e9..20e4155 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
+++ b/src/osgEarthDrivers/engine_mp/MPTerrainEngineDriver.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,47 +16,47 @@
  * 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 "QuadTreeTerrainEngineOptions"
+#include "MPTerrainEngineNode"
+#include "MPTerrainEngineOptions"
 #include <osgEarth/Registry>
 #include <osgDB/FileNameUtils>
 #include <osgDB/FileUtils>
 #include <osgDB/Registry>
 #include <string>
 
-#define LC "[engine_quadtree driver] "
+#define LC "[engine_mp driver] "
 
 using namespace osgEarth::Drivers;
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 
 /**
- * osgEarth driver for the QuadTree terrain engine.
+ * osgEarth driver for the MP terrain engine.
  *
  * TODO LIST:
  * - integrate support for Quick Release of GL objects
  * - integrate support for LOD Blending (access to parent state set)
  * - consider TileNodeCompiler cacheing of TexCoord arrays and other tile-shareable data
  */
-class osgEarth_QuadTreeTerrainEngineDriver : public osgDB::ReaderWriter
+class osgEarth_MPTerrainEngineDriver : public osgDB::ReaderWriter
 {
 public:
-    osgEarth_QuadTreeTerrainEngineDriver() {}
+    osgEarth_MPTerrainEngineDriver() {}
 
     virtual const char* className()
     {
-        return "osgEarth QuadTree Terrain Engine";
+        return "osgEarth MP Terrain Engine";
     }
 
     virtual bool acceptsExtension(const std::string& extension) const
     {
         return
-            osgDB::equalCaseInsensitive( extension, "osgearth_engine_quadtree" ) ||
-            osgDB::equalCaseInsensitive( extension, "osgearth_engine_quadtree_tile" );
+            osgDB::equalCaseInsensitive( extension, "osgearth_engine_mp" ) ||
+            osgDB::equalCaseInsensitive( extension, "osgearth_engine_mp_tile" );
     }
 
     virtual ReadResult readObject(const std::string& uri, const Options* options) const
     {
-        if ( "osgearth_engine_quadtree" == osgDB::getFileExtension( uri ) )
+        if ( "osgearth_engine_mp" == osgDB::getFileExtension( uri ) )
         {
             if ( "earth" == osgDB::getNameLessExtension( osgDB::getFileExtension( uri ) ) )
             {
@@ -64,9 +64,9 @@ public:
             }
             else
             {
-                QuadTreeTerrainEngineOptions terrainOpts;
+                MPTerrainEngineOptions terrainOpts;
                 OE_INFO << LC << "Activated!" << std::endl;
-                return ReadResult( new QuadTreeTerrainEngineNode() );
+                return ReadResult( new MPTerrainEngineNode() );
             }
         }
         else
@@ -81,7 +81,7 @@ public:
         static double s_tileTime = 0.0;
         static osg::Timer_t s_startTime;
 
-        if ( "osgearth_engine_quadtree_tile" == osgDB::getFileExtension(uri) )
+        if ( "osgearth_engine_mp_tile" == osgDB::getFileExtension(uri) )
         {
             if ( s_tileCount == 0 )
                 s_startTime = osg::Timer::instance()->tick();
@@ -101,8 +101,8 @@ public:
             sscanf(tileDef.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &engineID);
 
             // find the appropriate engine:
-            osg::ref_ptr<QuadTreeTerrainEngineNode> engineNode;
-            QuadTreeTerrainEngineNode::getEngineByUID( (UID)engineID, engineNode );
+            osg::ref_ptr<MPTerrainEngineNode> engineNode;
+            MPTerrainEngineNode::getEngineByUID( (UID)engineID, engineNode );
             if ( engineNode.valid() )
             {
                 osg::Timer_t start = osg::Timer::instance()->tick();
@@ -146,4 +146,4 @@ public:
     }
 };
 
-REGISTER_OSGPLUGIN(osgearth_engine_quadtree, osgEarth_QuadTreeTerrainEngineDriver)
+REGISTER_OSGPLUGIN(osgearth_engine_mp, osgEarth_MPTerrainEngineDriver)
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode b/src/osgEarthDrivers/engine_mp/MPTerrainEngineNode
similarity index 74%
copy from src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
copy to src/osgEarthDrivers/engine_mp/MPTerrainEngineNode
index 4a5b50c..abfdec6 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
+++ b/src/osgEarthDrivers/engine_mp/MPTerrainEngineNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -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_ENGINE_QUADTREE_ENGINE_NODE_H
-#define OSGEARTH_ENGINE_QUADTREE_ENGINE_NODE_H 1
+#ifndef OSGEARTH_ENGINE_MP_ENGINE_NODE_H
+#define OSGEARTH_ENGINE_MP_ENGINE_NODE_H 1
 
 #include <osgEarth/TerrainEngineNode>
 #include <osgEarth/TextureCompositor>
@@ -25,7 +25,7 @@
 #include <osgEarth/Revisioning>
 #include <osgEarth/ThreadingUtils>
 
-#include "QuadTreeTerrainEngineOptions"
+#include "MPTerrainEngineOptions"
 #include "KeyNodeFactory"
 #include "TileModelFactory"
 #include "TileModelCompiler"
@@ -37,14 +37,14 @@
 
 using namespace osgEarth;
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
-    class QuadTreeTerrainEngineNode : public TerrainEngineNode
+    class MPTerrainEngineNode : public TerrainEngineNode
     {
     public:
-        QuadTreeTerrainEngineNode();
-        META_Node(osgEarth,QuadTreeTerrainEngineNode);
-        virtual ~QuadTreeTerrainEngineNode();
+        MPTerrainEngineNode();
+        META_Node(osgEarth,MPTerrainEngineNode);
+        virtual ~MPTerrainEngineNode();
 
     public:
         osg::Node* createNode(const TileKey& key);
@@ -69,31 +69,35 @@ namespace osgEarth_engine_quadtree
         UID getUID() const;
 
     public: // statics    
-        static void registerEngine( QuadTreeTerrainEngineNode* engineNode );
+        static void registerEngine( MPTerrainEngineNode* engineNode );
         static void unregisterEngine( UID uid );
-        static void getEngineByUID( UID uid, osg::ref_ptr<QuadTreeTerrainEngineNode>& output );
+        static void getEngineByUID( UID uid, osg::ref_ptr<MPTerrainEngineNode>& output );
 
     public:
         class ElevationChangedCallback : public ElevationLayerCallback
         {
         public:
-            ElevationChangedCallback( QuadTreeTerrainEngineNode* terrain );
+            ElevationChangedCallback( MPTerrainEngineNode* terrain );
 
            virtual void onVisibleChanged( TerrainLayer* layer );
 
-            QuadTreeTerrainEngineNode* _terrain;
-            friend class QuadTreeTerrainEngineNode;
+            MPTerrainEngineNode* _terrain;
+            friend class MPTerrainEngineNode;
         };
 
     protected:
         virtual void onVerticalScaleChanged();
 
+        // override from TerrainEngineNode
+        virtual void updateTextureCombining() { updateShaders(); }
+
     private:
         void init();
         void syncMapModel();
 
         // Reloads all the tiles in the terrain due to a data model change
         void refresh();
+        void createTerrain();
 
         void addImageLayer( ImageLayer* layer );
         void addElevationLayer( ElevationLayer* layer );
@@ -104,16 +108,17 @@ namespace osgEarth_engine_quadtree
         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 updateShaders(); 
 
     private:
-        osgEarth::Drivers::QuadTreeTerrainEngineOptions _terrainOptions;
+        osgEarth::Drivers::MPTerrainEngineOptions _terrainOptions;
 
         class TerrainNode* _terrain;
         UID                _uid;
         Revision           _shaderLibRev;
+        bool               _batchUpdateInProgress;
+        bool               _refreshRequired;
+        bool               _shaderUpdateRequired;
 
         osg::ref_ptr< ElevationChangedCallback > _elevationCallback;
 
@@ -129,14 +134,15 @@ namespace osgEarth_engine_quadtree
         osg::Timer _timer;
         unsigned   _tileCount;
         double     _tileCreationTime;
+        int        _textureImageUnit;
 
         osg::Uniform* _verticalScaleUniform;
 
         osg::ref_ptr< TileModelFactory > _tileModelFactory;
 
-        QuadTreeTerrainEngineNode( const QuadTreeTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
+        MPTerrainEngineNode( const MPTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_ENGINE_NODE_H
+#endif // OSGEARTH_ENGINE_MP_ENGINE_NODE_H
diff --git a/src/osgEarthDrivers/engine_mp/MPTerrainEngineNode.cpp b/src/osgEarthDrivers/engine_mp/MPTerrainEngineNode.cpp
new file mode 100644
index 0000000..e290e99
--- /dev/null
+++ b/src/osgEarthDrivers/engine_mp/MPTerrainEngineNode.cpp
@@ -0,0 +1,730 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 "MPTerrainEngineNode"
+#include "SerialKeyNodeFactory"
+#include "TerrainNode"
+#include "TileModelFactory"
+#include "TileModelCompiler"
+
+#include <osgEarth/HeightFieldUtils>
+#include <osgEarth/ImageUtils>
+#include <osgEarth/Registry>
+#include <osgEarth/VirtualProgram>
+#include <osgEarth/ShaderFactory>
+#include <osgEarth/MapModelChange>
+
+#include <osg/TexEnv>
+#include <osg/TexEnvCombine>
+#include <osg/PagedLOD>
+#include <osg/Timer>
+#include <osg/Depth>
+#include <osg/BlendFunc>
+
+#define LC "[MPTerrainEngineNode] "
+
+using namespace osgEarth_engine_mp;
+using namespace osgEarth;
+
+//------------------------------------------------------------------------
+
+namespace
+{
+    // adapter that lets MPTerrainEngineNode listen to Map events
+    struct MPTerrainEngineNodeMapCallbackProxy : public MapCallback
+    {
+        MPTerrainEngineNodeMapCallbackProxy(MPTerrainEngineNode* node) : _node(node) { }
+        osg::observer_ptr<MPTerrainEngineNode> _node;
+
+        void onMapInfoEstablished( const MapInfo& mapInfo ) {
+            _node->onMapInfoEstablished( mapInfo );
+        }
+
+        void onMapModelChanged( const MapModelChange& change ) {
+            _node->onMapModelChanged( change );
+        }
+    };
+}
+
+//---------------------------------------------------------------------------
+
+static Threading::ReadWriteMutex s_engineNodeCacheMutex;
+//Caches the MapNodes that have been created
+typedef std::map<UID, osg::observer_ptr<MPTerrainEngineNode> > EngineNodeCache;
+
+static
+EngineNodeCache& getEngineNodeCache()
+{
+    static EngineNodeCache s_cache;
+    return s_cache;
+}
+
+void
+MPTerrainEngineNode::registerEngine(MPTerrainEngineNode* engineNode)
+{
+    Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex );
+    getEngineNodeCache()[engineNode->_uid] = engineNode;
+    OE_DEBUG << LC << "Registered engine " << engineNode->_uid << std::endl;
+}
+
+void
+MPTerrainEngineNode::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
+MPTerrainEngineNode::getEngineByUID( UID uid, osg::ref_ptr<MPTerrainEngineNode>& output )
+{
+    Threading::ScopedReadLock sharedLock( s_engineNodeCacheMutex );
+    EngineNodeCache::const_iterator k = getEngineNodeCache().find( uid );
+    if (k != getEngineNodeCache().end())
+        output = k->second.get();
+}
+
+UID
+MPTerrainEngineNode::getUID() const
+{
+    return _uid;
+}
+
+//------------------------------------------------------------------------
+
+MPTerrainEngineNode::ElevationChangedCallback::ElevationChangedCallback( MPTerrainEngineNode* terrain ):
+_terrain( terrain )
+{
+    //nop
+}
+
+void
+MPTerrainEngineNode::ElevationChangedCallback::onVisibleChanged( TerrainLayer* layer )
+{    
+    osgEarth::Registry::instance()->clearBlacklist();
+    _terrain->refresh();
+}
+
+//------------------------------------------------------------------------
+
+MPTerrainEngineNode::MPTerrainEngineNode() :
+TerrainEngineNode     ( ),
+_terrain              ( 0L ),
+_update_mapf          ( 0L ),
+_tileCount            ( 0 ),
+_tileCreationTime     ( 0.0 ),
+_textureImageUnit     ( 0 ),
+_batchUpdateInProgress( false ),
+_refreshRequired      ( false ),
+_shaderUpdateRequired ( false )
+{
+    _uid = Registry::instance()->createUID();
+
+    // install an elevation callback so we can update elevation data
+    _elevationCallback = new ElevationChangedCallback( this );
+}
+
+MPTerrainEngineNode::~MPTerrainEngineNode()
+{
+    unregisterEngine( _uid );
+
+    if ( _update_mapf )
+    {
+        delete _update_mapf;
+    }
+}
+
+void
+MPTerrainEngineNode::preInitialize( const Map* map, const TerrainOptions& options )
+{
+    TerrainEngineNode::preInitialize( map, options );
+
+    // override the compositor technique because we want to do unit
+    // reservations but nothing else.
+    getTextureCompositor()->setTechnique( 0L );
+}
+
+void
+MPTerrainEngineNode::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, "mp-update" );
+
+    // merge in the custom options:
+    _terrainOptions.merge( options );
+
+    // a shared registry for tile nodes in the scene graph.
+    _liveTiles = new TileNodeRegistry("live");
+
+    // set up a registry for quick release:
+    if ( _terrainOptions.quickReleaseGLObjects() == true )
+    {
+        _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() )
+    {
+        // 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 )
+    {
+        this->getTextureCompositor()->reserveTextureImageUnit( _textureImageUnit );
+        updateShaders();
+    }
+
+    // install a layer callback for processing further map actions:
+    map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(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() );
+    }
+
+    // register this instance to the osgDB plugin can find it.
+    registerEngine( this );
+
+    // now that we have a map, set up to recompute the bounds
+    dirtyBound();
+}
+
+
+osg::BoundingSphere
+MPTerrainEngineNode::computeBound() const
+{
+    if ( _terrain && _terrain->getNumChildren() > 0 )
+    {
+        return _terrain->getBound();
+    }
+    else
+    {
+        return TerrainEngineNode::computeBound();
+    }
+}
+
+
+void
+MPTerrainEngineNode::refresh()
+{
+    if ( _batchUpdateInProgress )
+    {
+        _refreshRequired = true;
+    }
+    else
+    {
+        // remove the old one:
+        this->removeChild( _terrain );
+
+        // and create a new one.
+        createTerrain();
+
+        _refreshRequired = false;
+    }
+}
+
+void
+MPTerrainEngineNode::onMapInfoEstablished( const MapInfo& mapInfo )
+{
+    OE_INFO << LC << "Sample ratio = " << _terrainOptions.heightFieldSampleRatio().value() << std::endl;
+
+    createTerrain();
+}
+
+void
+MPTerrainEngineNode::createTerrain()
+{
+    // scrub the heightfield cache.
+    if (_tileModelFactory)
+        _tileModelFactory->getHeightFieldCache()->clear();
+
+    // New terrain
+    _terrain = new TerrainNode( _deadTiles.get() );
+    this->addChild( _terrain );
+
+    // Enable blending on the terrain node; this will result in the underlying
+    // "empty" globe being transparent instead of white.
+    if (_terrainOptions.enableBlending().value())
+    {
+        _terrain->getOrCreateStateSet()->setMode(GL_BLEND , osg::StateAttribute::ON);
+    }
+
+    // Factory to create the root keys:
+    KeyNodeFactory* factory = getKeyNodeFactory();
+
+    // 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()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys );
+
+    // create a root node for each root tile key.
+    OE_INFO << LC << "Creating root keys (" << keys.size() << ")" << std::flush;
+
+    for( unsigned i=0; i<keys.size(); ++i )
+    {
+        osg::Node* node = factory->createRootNode( keys[i] );
+        OE_INFO_CONTINUE << "." << std::flush;
+        if ( node )
+            _terrain->addChild( node );
+        else
+            OE_WARN << LC << "Couldn't make tile for root key: " << keys[i].str() << std::endl;
+    }
+
+    OE_INFO_CONTINUE << "done." << std::endl;
+
+    updateShaders();
+}
+
+
+KeyNodeFactory*
+MPTerrainEngineNode::getKeyNodeFactory()
+{
+    osg::ref_ptr<KeyNodeFactory>& knf = _perThreadKeyNodeFactories.get(); // thread-safe get
+    if ( !knf.valid() )
+    {
+        // create a compiler for compiling tile models into geometry
+        bool optimizeTriangleOrientation = 
+            getMap()->getMapOptions().elevationInterpolation() != INTERP_TRIANGULATE;
+
+        // A compiler specific to this thread:
+        TileModelCompiler* compiler = new TileModelCompiler(
+            _update_mapf->terrainMaskLayers(),
+            _textureImageUnit,
+            optimizeTriangleOrientation,
+            _terrainOptions );
+
+        // initialize a key node factory.
+        knf = new SerialKeyNodeFactory( 
+            _tileModelFactory.get(),
+            compiler,
+            _liveTiles.get(),
+            _deadTiles.get(),
+            _terrainOptions, 
+            MapInfo( getMap() ),
+            _terrain, 
+            _uid );
+    }
+
+    return knf.get();
+}
+
+
+osg::Node*
+MPTerrainEngineNode::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;
+
+    osg::Node* result =  getKeyNodeFactory()->createNode( key );
+    return result;
+}
+
+osg::Node*
+MPTerrainEngineNode::createTile( const TileKey& key )
+{
+    return getKeyNodeFactory()->createNode( key );
+}
+
+
+void
+MPTerrainEngineNode::onMapModelChanged( const MapModelChange& change )
+{
+    if ( change.getAction() == MapModelChange::BEGIN_BATCH_UPDATE )
+    {
+        _batchUpdateInProgress = true;
+    }
+
+    else if ( change.getAction() == MapModelChange::END_BATCH_UPDATE )
+    {
+        _batchUpdateInProgress = false;
+
+        if ( _refreshRequired )
+            refresh();
+
+        if ( _shaderUpdateRequired )
+            updateShaders();
+    }
+
+    else
+    {
+        // update the thread-safe map model copy:
+        _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;
+            }
+        }
+    }
+}
+
+
+void
+MPTerrainEngineNode::addImageLayer( ImageLayer* layerAdded )
+{
+    refresh();
+}
+
+
+void
+MPTerrainEngineNode::removeImageLayer( ImageLayer* layerRemoved )
+{
+    refresh();
+}
+
+void
+MPTerrainEngineNode::moveImageLayer( unsigned int oldIndex, unsigned int newIndex )
+{
+    updateShaders();
+}
+
+void
+MPTerrainEngineNode::addElevationLayer( ElevationLayer* layer )
+{
+    if ( !layer )
+        return;
+
+    layer->addCallback( _elevationCallback.get() );
+
+    refresh();
+}
+
+void
+MPTerrainEngineNode::removeElevationLayer( ElevationLayer* layerRemoved )
+{
+    layerRemoved->removeCallback( _elevationCallback.get() );
+
+    refresh();
+}
+
+void
+MPTerrainEngineNode::moveElevationLayer( unsigned int oldIndex, unsigned int newIndex )
+{
+    refresh();
+}
+
+void
+MPTerrainEngineNode::validateTerrainOptions( TerrainOptions& options )
+{
+    TerrainEngineNode::validateTerrainOptions( options );
+    
+    //nop for now.
+    //note: to validate plugin-specific features, we would create an MPTerrainEngineOptions
+    // and do the validation on that. You would then re-integrate it by calling
+    // options.mergeConfig( osgTerrainOptions ).
+}
+
+
+// Generates the main shader code for rendering the terrain.
+void
+MPTerrainEngineNode::updateShaders()
+{
+    if ( _batchUpdateInProgress )
+    {
+        _shaderUpdateRequired = true;
+    }
+    else
+    {
+        osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet();
+
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setName( "engine_mp:TerrainNode" );
+        terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
+
+        // Vertex shader template:
+        std::string vs =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "varying vec4 oe_layer_tc;\n"
+            "void oe_mp_setup_coloring(inout vec4 VertexModel) \n"
+            "{ \n"
+            "    oe_layer_tc = __GL_MULTITEXCOORD__;\n"
+            "}\n";
+
+        // Fragment shader for normal blending:
+        std::string fs =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "varying vec4 oe_layer_tc; \n"
+            "uniform sampler2D oe_layer_tex; \n"
+            "uniform int oe_layer_uid; \n"
+            "uniform int oe_layer_order; \n"
+            "uniform float oe_layer_opacity; \n"
+            "void oe_mp_apply_coloring( inout vec4 color ) \n"
+            "{ \n"
+            "    vec4 texel; \n"
+            "    if ( oe_layer_uid >= 0 ) \n"
+            "        texel = texture2D(oe_layer_tex, oe_layer_tc.st); \n"
+            "    else \n"
+            "        texel = color; \n"
+            "    texel.a *= oe_layer_opacity; \n"
+            "    if (oe_layer_order == 0 ) \n"
+            "        color = vec4(color.rgb*(1.0-texel.a) + texel.rgb*texel.a, color.a); \n"
+            "    else \n"
+            "        color = texel; \n"
+            "} \n";
+
+        // Fragment shader with pre-multiplied alpha blending:
+        std::string fs_pma =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "varying vec4 oe_layer_tc; \n"
+            "uniform sampler2D oe_layer_tex; \n"
+            "uniform int oe_layer_uid; \n"
+            "uniform int oe_layer_order; \n"
+            "uniform float oe_layer_opacity; \n"
+            "void oe_mp_apply_coloring_pma( inout vec4 color ) \n"
+            "{ \n"
+            "    vec4 texelpma; \n"
+
+            // a UID < 0 means no texture.
+            "    if ( oe_layer_uid >= 0 ) \n"
+            "        texelpma = texture2D(oe_layer_tex, oe_layer_tc.st) * oe_layer_opacity; \n"
+            "    else \n"
+            "        texelpma = color * color.a * oe_layer_opacity; \n" // to PMA.
+
+            // first layer must PMA-blend with the globe color.
+            "    if (oe_layer_order == 0) { \n"
+            "        color *= color.a; \n"
+            "        color = vec4(texelpma.rgb + color.rgb*(1.0-texelpma.a), color.a); \n"
+            "    } \n"
+
+            "    else { \n"
+            "        color = texelpma; \n"
+            "    } \n"
+            "} \n";
+
+        // Color filter frag function:
+        std::string fs_colorfilters =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "uniform int oe_layer_uid; \n"
+            "__COLOR_FILTER_HEAD__"
+            "void oe_mp_apply_filters(inout vec4 color) \n"
+            "{ \n"
+                "__COLOR_FILTER_BODY__"
+            "} \n";
+
+
+        // install the gl_MultiTexCoord* variable that uses the proper texture
+        // image unit:
+        replaceIn( vs, "__GL_MULTITEXCOORD__", Stringify() << "gl_MultiTexCoord" << _textureImageUnit );
+
+        vp->setFunction( "oe_mp_setup_coloring", vs, ShaderComp::LOCATION_VERTEX_MODEL, 0.0 );
+
+        if ( _terrainOptions.premultipliedAlpha() == true )
+            vp->setFunction( "oe_mp_apply_coloring_pma", fs_pma, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 );
+        else
+            vp->setFunction( "oe_mp_apply_coloring", fs, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 );
+
+
+        // assemble color filter code snippets.
+        bool haveColorFilters = false;
+        {
+            std::stringstream cf_head;
+            std::stringstream cf_body;
+            const char* I = "    ";
+
+            if ( _terrainOptions.premultipliedAlpha() == true )
+            {
+                // un-PMA the color before passing it to the color filters.
+                cf_body << I << "if (color.a > 0.0) color.rgb /= color.a; \n";
+            }
+
+            // second, install the per-layer color filter functions.
+            bool ifStarted = false;
+            int numImageLayers = _update_mapf->imageLayers().size();
+            for( int i=0; i<numImageLayers; ++i )
+            {
+                ImageLayer* layer = _update_mapf->getImageLayerAt(i);
+                if ( layer->getEnabled() )
+                {
+                    const ColorFilterChain& chain = layer->getColorFilters();
+                    if ( chain.size() > 0 )
+                    {
+                        haveColorFilters = true;
+                        if ( ifStarted ) cf_body << I << "else if ";
+                        else             cf_body << I << "if ";
+                        cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n";
+                        for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j )
+                        {
+                            const ColorFilter* filter = j->get();
+                            cf_head << "void " << filter->getEntryPointFunctionName() << "(in int slot, inout vec4 color);\n";
+                            cf_body << I << I << filter->getEntryPointFunctionName() << "(" << _textureImageUnit << ", color);\n";
+                            filter->install( terrainStateSet );
+                        }
+                        cf_body << I << "}\n";
+                        ifStarted = true;
+                    }
+                }
+            }
+
+            if ( _terrainOptions.premultipliedAlpha() == true )
+            {
+                // re-PMA the color after it passes through the color filters.
+                cf_body << I << "color.rgb *= color.a; \n";
+            }
+
+            if ( haveColorFilters )
+            {
+                std::string cf_head_str, cf_body_str;
+                cf_head_str = cf_head.str();
+                cf_body_str = cf_body.str();
+
+                replaceIn( fs_colorfilters, "__COLOR_FILTER_HEAD__", cf_head_str );
+                replaceIn( fs_colorfilters, "__COLOR_FILTER_BODY__", cf_body_str );
+
+                vp->setFunction( "oe_mp_apply_filters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 );
+            }
+        }
+
+
+
+        if ( _terrainOptions.premultipliedAlpha() == true )
+        {
+            // activate PMA blending.
+            terrainStateSet->setAttributeAndModes( 
+                new osg::BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA),
+                osg::StateAttribute::ON );
+        }
+        else
+        {
+            // activate standard mix blending.
+            terrainStateSet->setAttributeAndModes( 
+                new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA),
+                osg::StateAttribute::ON );
+        }
+
+        // required for multipass tile rendering to work
+        terrainStateSet->setAttributeAndModes(
+            new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) );
+
+        // binding for the terrain texture
+        terrainStateSet->getOrCreateUniform( 
+            "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _textureImageUnit );
+
+        // uniform that controls per-layer opacity
+        terrainStateSet->getOrCreateUniform(
+            "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f );
+
+        // uniform that conveys the layer UID to the shaders; necessary
+        // for per-layer branching (like color filters)
+        // UID -1 => no image layer (no texture)
+        terrainStateSet->getOrCreateUniform(
+            "oe_layer_uid", osg::Uniform::INT )->set( -1 );
+
+        // uniform that conveys the render order, since the shaders
+        // need to know which is the first layer in order to blend properly
+        terrainStateSet->getOrCreateUniform(
+            "oe_layer_order", osg::Uniform::INT )->set( 0 );
+
+        _shaderUpdateRequired = false;
+    }
+}
+
+
+namespace
+{
+    class UpdateElevationVisitor : public osg::NodeVisitor
+    {
+    public:
+        UpdateElevationVisitor( TileModelCompiler* compiler ):
+          osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+          _compiler(compiler)
+          {}
+
+          void apply(osg::Node& node)
+          {
+              TileNode* tile = dynamic_cast<TileNode*>(&node);
+              if (tile)
+              {
+                  tile->compile( _compiler );
+              }
+
+              traverse(node);
+          }
+
+          TileModelCompiler* _compiler;
+    };
+}
+
+
+void
+MPTerrainEngineNode::onVerticalScaleChanged()
+{
+    _terrainOptions.verticalScale() = getVerticalScale();
+    UpdateElevationVisitor visitor( getKeyNodeFactory()->getCompiler() );
+    this->accept(visitor);
+}
diff --git a/src/osgEarthDrivers/engine_mp/MPTerrainEngineOptions b/src/osgEarthDrivers/engine_mp/MPTerrainEngineOptions
new file mode 100644
index 0000000..075f546
--- /dev/null
+++ b/src/osgEarthDrivers/engine_mp/MPTerrainEngineOptions
@@ -0,0 +1,126 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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_MP_OPTIONS
+#define OSGEARTH_ENGINE_MP_OPTIONS 1
+
+#include <osgEarth/Common>
+#include <osgEarth/TerrainOptions>
+#include <osgEarthSymbology/Color>
+
+namespace osgEarth { namespace Drivers
+{
+    using namespace osgEarth;
+    using namespace osgEarth::Symbology;
+
+    /**
+     * Options for configuring the MP Engine driver.
+     */
+    class MPTerrainEngineOptions : public TerrainOptions // NO EXPORT (header-only)
+    {
+    public:
+        MPTerrainEngineOptions( const ConfigOptions& options =ConfigOptions() ) : TerrainOptions( options ),
+            _skirtRatio    ( 0.05 ),
+            _quickRelease  ( true ),
+            _lodFallOff    ( 0.0 ),
+            _normalizeEdges( true ),
+            _rangeMode     ( osg::LOD::DISTANCE_FROM_EYE_POINT ),
+            _tilePixelSize ( 256 ),
+            _premultAlpha  ( true ),
+            _color         ( Color::White )
+        {
+            setDriver( "mp" );
+            fromConfig( _conf );
+        }
+
+        /** dtor */
+        virtual ~MPTerrainEngineOptions() { }
+
+    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<bool>& normalizeEdges() { return _normalizeEdges; }
+        const optional<bool>& normalizeEdges() const { return _normalizeEdges; }
+
+        optional<osg::LOD::RangeMode>& rangeMode() { return _rangeMode;}
+        const optional<osg::LOD::RangeMode>& rangeMode() const { return _rangeMode;}
+
+        optional<float>& tilePixelSize() { return _tilePixelSize; }
+        const optional<float>& tilePixelSize() const { return _tilePixelSize; }
+
+        optional<bool>& premultipliedAlpha() { return _premultAlpha; }
+        const optional<bool>& premultipliedAlpha() const { return _premultAlpha; }
+
+        optional<Color>& color() { return _color; }
+        const optional<Color>& color() const { return _color; }
+
+    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( "normalize_edges", _normalizeEdges);
+            conf.updateIfSet( "tile_pixel_size", _tilePixelSize );
+            conf.updateIfSet( "range_mode", "PIXEL_SIZE_ON_SCREEN", _rangeMode, osg::LOD::PIXEL_SIZE_ON_SCREEN );
+            conf.updateIfSet( "range_mode", "DISTANCE_FROM_EYE_POINT", _rangeMode, osg::LOD::DISTANCE_FROM_EYE_POINT);
+            conf.updateIfSet( "premultiplied_alpha", _premultAlpha );
+            conf.updateIfSet( "color", _color );
+
+            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( "normalize_edges", _normalizeEdges );
+            conf.getIfSet( "tile_pixel_size", _tilePixelSize );
+
+            conf.getIfSet( "range_mode", "PIXEL_SIZE_ON_SCREEN", _rangeMode, osg::LOD::PIXEL_SIZE_ON_SCREEN );
+            conf.getIfSet( "range_mode", "DISTANCE_FROM_EYE_POINT", _rangeMode, osg::LOD::DISTANCE_FROM_EYE_POINT);
+            conf.getIfSet( "premultiplied_alpha", _premultAlpha );
+            conf.getIfSet( "color", _color );
+        }
+
+        optional<float>               _skirtRatio;
+        optional<bool>                _quickRelease;
+        optional<float>               _lodFallOff;
+        optional<bool>                _normalizeEdges;
+        optional<osg::LOD::RangeMode> _rangeMode;
+        optional<float>               _tilePixelSize;
+        optional<bool>                _premultAlpha;
+        optional<Color>               _color;
+    };
+
+} } // namespace osgEarth::Drivers
+
+#endif // OSGEARTH_ENGINE_MP_OPTIONS
diff --git a/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects b/src/osgEarthDrivers/engine_mp/QuickReleaseGLObjects
similarity index 90%
copy from src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
copy to src/osgEarthDrivers/engine_mp/QuickReleaseGLObjects
index 9a32417..8f5c121 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
+++ b/src/osgEarthDrivers/engine_mp/QuickReleaseGLObjects
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,14 +16,14 @@
 * 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_QUICK_RELEASE_GL_OBJECTS
-#define OSGEARTH_ENGINE_QUADTREE_QUICK_RELEASE_GL_OBJECTS 1
+#ifndef OSGEARTH_ENGINE_MP_QUICK_RELEASE_GL_OBJECTS
+#define OSGEARTH_ENGINE_MP_QUICK_RELEASE_GL_OBJECTS 1
 
 #include "Common"
 #include "TileNodeRegistry"
 #include <osg/Camera>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     /**
      * A draw callback to calls another, nested draw callback.
@@ -86,6 +86,6 @@ namespace osgEarth_engine_quadtree
         osg::ref_ptr<TileNodeRegistry> _tilesToRelease;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_QUICK_RELEASE_GL_OBJECTS
+#endif // OSGEARTH_ENGINE_MP_QUICK_RELEASE_GL_OBJECTS
diff --git a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory b/src/osgEarthDrivers/engine_mp/SerialKeyNodeFactory
similarity index 85%
copy from src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
copy to src/osgEarthDrivers/engine_mp/SerialKeyNodeFactory
index 954c602..e8b0502 100644
--- a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_mp/SerialKeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -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_ENGINE_QUADTREE_SERIAL_KEY_NODE_FACTORY
-#define OSGEARTH_ENGINE_QUADTREE_SERIAL_KEY_NODE_FACTORY 1
+#ifndef OSGEARTH_ENGINE_MP_SERIAL_KEY_NODE_FACTORY
+#define OSGEARTH_ENGINE_MP_SERIAL_KEY_NODE_FACTORY 1
 
 #include "Common"
 #include "KeyNodeFactory"
@@ -29,7 +29,7 @@
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     class SerialKeyNodeFactory : public KeyNodeFactory
     {
@@ -39,7 +39,7 @@ namespace osgEarth_engine_quadtree
             TileModelCompiler*                  modelCompiler,
             TileNodeRegistry*                   liveTiles,
             TileNodeRegistry*                   deadTiles,
-            const QuadTreeTerrainEngineOptions& options,
+            const MPTerrainEngineOptions& options,
             const MapInfo&                      mapInfo,
             TerrainNode*                        terrain,
             UID                                 engineUID );
@@ -63,12 +63,12 @@ namespace osgEarth_engine_quadtree
         osg::ref_ptr<TileModelCompiler>     _modelCompiler;
         osg::ref_ptr<TileNodeRegistry>      _liveTiles;
         osg::ref_ptr<TileNodeRegistry>      _deadTiles;
-        const QuadTreeTerrainEngineOptions& _options;
+        const MPTerrainEngineOptions& _options;
         const MapInfo                       _mapInfo;
         osg::ref_ptr< TerrainNode >         _terrain;
         UID                                 _engineUID;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_SERIAL_KEY_NODE_FACTORY
+#endif // OSGEARTH_ENGINE_MP_SERIAL_KEY_NODE_FACTORY
diff --git a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_mp/SerialKeyNodeFactory.cpp
similarity index 87%
copy from src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
copy to src/osgEarthDrivers/engine_mp/SerialKeyNodeFactory.cpp
index 082705b..107c26f 100644
--- a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_mp/SerialKeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -30,7 +30,7 @@
 
 #include <osgEarth/MapNode>
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 using namespace OpenThreads;
 
@@ -41,7 +41,7 @@ SerialKeyNodeFactory::SerialKeyNodeFactory(TileModelFactory*        modelFactory
                                            TileModelCompiler*       modelCompiler,
                                            TileNodeRegistry*        liveTiles,
                                            TileNodeRegistry*        deadTiles,
-                                           const QuadTreeTerrainEngineOptions& options,
+                                           const MPTerrainEngineOptions& options,
                                            const MapInfo&           mapInfo,
                                            TerrainNode*             terrain,
                                            UID                      engineUID ) :
@@ -68,7 +68,7 @@ SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileH
     tileNode->compile( _modelCompiler.get() );
 
     // assemble a URI for this tile's child group:
-    std::string uri = Stringify() << model->_tileKey.str() << "." << _engineUID << ".osgearth_engine_quadtree_tile";
+    std::string uri = Stringify() << model->_tileKey.str() << "." << _engineUID << ".osgearth_engine_mp_tile";
 
     osg::Node* result = 0L;
 
@@ -78,24 +78,17 @@ SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileH
     // 3. We are still below the maximim LOD.
     bool wrapInPagedLOD =
         (tileHasRealData || (_options.minLOD().isSet() && model->_tileKey.getLOD() < *_options.minLOD())) &&
-        //(tileHasRealData || _options.minLOD().isSet()) &&
         !osgEarth::Registry::instance()->isBlacklisted( uri ) &&
         model->_tileKey.getLOD() < *_options.maxLOD();
 
     if ( wrapInPagedLOD )
     {
         osg::BoundingSphere bs = tileNode->getBound();
+      
         float maxRange = FLT_MAX;
         
-#if 0
-        //Compute the min range based on the actual bounds of the tile.  This can break down if you have very high resolution
-        //data with elevation variations and you can run out of memory b/c the elevation change is greater than the actual size of the tile so you end up
-        //inifinitely subdividing (or at least until you run out of data or memory)
-        double minRange = bs.radius() * _options.minTileRangeFactor().value();
-#else        
-        //double origMinRange = bs.radius() * _options.minTileRangeFactor().value();        
         //Compute the min range based on the 2D size of the tile
-        GeoExtent extent = model->_tileKey.getExtent();        
+        GeoExtent extent = model->_tileKey.getExtent();
         GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE);
         GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE);
         osg::Vec3d ll, ur;
@@ -103,15 +96,31 @@ SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileH
         upperRight.toWorld( ur );
         double radius = (ur - ll).length() / 2.0;
         float minRange = (float)(radius * _options.minTileRangeFactor().value());
-#endif
 
+        
         // create a PLOD so we can keep subdividing:
         osg::PagedLOD* plod = new CustomPagedLOD( _liveTiles.get(), _deadTiles.get() );
         plod->setCenter( bs.center() );
-        plod->addChild( tileNode, minRange, maxRange );
-
+        plod->addChild( tileNode );
+        plod->setRangeMode( *_options.rangeMode() );
         plod->setFileName( 1, uri );
-        plod->setRange   ( 1, 0, minRange );
+  
+
+        if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN)
+        {
+            static const float sqrt2 = sqrt(2.0f);
+
+            minRange = 0;
+            maxRange = (*_options.tilePixelSize()) * sqrt2;
+            plod->setRange( 0, minRange, maxRange  );
+            plod->setRange( 1, maxRange, FLT_MAX );            
+        }
+        else
+        {
+            plod->setRange( 0, minRange, maxRange );                
+            plod->setRange( 1, 0, minRange );        
+        }        
+                        
 
         plod->setUserData( new MapNode::TileRangeData(minRange, maxRange) );
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TerrainNode b/src/osgEarthDrivers/engine_mp/TerrainNode
similarity index 82%
copy from src/osgEarthDrivers/engine_quadtree/TerrainNode
copy to src/osgEarthDrivers/engine_mp/TerrainNode
index b9243ce..0f4558e 100644
--- a/src/osgEarthDrivers/engine_quadtree/TerrainNode
+++ b/src/osgEarthDrivers/engine_mp/TerrainNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,13 +16,13 @@
 * 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_TERRAIN_NODE
-#define OSGEARTH_ENGINE_QUADTREE_TERRAIN_NODE 1
+#ifndef OSGEARTH_ENGINE_MP_TERRAIN_NODE
+#define OSGEARTH_ENGINE_MP_TERRAIN_NODE 1
 
 #include "Common"
 #include "TileNodeRegistry"
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     class TileFactory;
 
@@ -30,7 +30,7 @@ namespace osgEarth_engine_quadtree
     using namespace osgEarth::Drivers;
 
     /**
-     * Parent node for the TileNode quadtree hierarchy.
+     * Parent node for the TileNode mp hierarchy.
      */
     class TerrainNode : public osg::Group
     {
@@ -55,6 +55,6 @@ namespace osgEarth_engine_quadtree
         bool _quickReleaseCallbackInstalled;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_TERRAIN_NODE
+#endif // OSGEARTH_ENGINE_MP_TERRAIN_NODE
diff --git a/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp b/src/osgEarthDrivers/engine_mp/TerrainNode.cpp
similarity index 82%
copy from src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
copy to src/osgEarthDrivers/engine_mp/TerrainNode.cpp
index 143c200..8192e3c 100644
--- a/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_mp/TerrainNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -29,7 +29,7 @@
 #include <osg/Node>
 #include <osgGA/EventVisitor>
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 using namespace OpenThreads;
 
@@ -61,9 +61,17 @@ TerrainNode::traverse( osg::NodeVisitor &nv )
             osg::Camera* cam = findFirstParentOfType<osg::Camera>( this );
             if ( cam )
             {
+                // get the installed PDC so we can nest them:
+                osg::Camera::DrawCallback* cbToNest = cam->getPostDrawCallback();
+
+                // if it's another QR callback, we'll just replace it.
+                QuickReleaseGLObjects* previousQR = dynamic_cast<QuickReleaseGLObjects*>(cbToNest);
+                if ( previousQR )
+                    cbToNest = previousQR->_next.get();
+
                 cam->setPostDrawCallback( new QuickReleaseGLObjects(
                     _tilesToQuickRelease.get(),
-                    cam->getPostDrawCallback() ) );
+                    cbToNest ) );
 
                 _quickReleaseCallbackInstalled = true;
                 OE_INFO << LC << "Quick release enabled" << std::endl;
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModel b/src/osgEarthDrivers/engine_mp/TileModel
similarity index 84%
copy from src/osgEarthDrivers/engine_quadtree/TileModel
copy to src/osgEarthDrivers/engine_mp/TileModel
index cc093bc..488d846 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModel
+++ b/src/osgEarthDrivers/engine_mp/TileModel
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -17,11 +17,12 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 
-#ifndef OSGEARTH_ENGINE_QUADTREE_TILE_MODEL
-#define OSGEARTH_ENGINE_QUADTREE_TILE_MODEL 1
+#ifndef OSGEARTH_ENGINE_MP_TILE_MODEL
+#define OSGEARTH_ENGINE_MP_TILE_MODEL 1
 
 #include "Common"
 #include <osgEarth/Common>
+#include <osgEarth/Map>
 #include <osgEarth/ImageLayer>
 #include <osgEarth/TileKey>
 #include <osgTerrain/Locator>
@@ -30,7 +31,7 @@
 #include <osg/StateSet>
 #include <map>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     using namespace osgEarth;
 
@@ -96,15 +97,17 @@ namespace osgEarth_engine_quadtree
 
             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) { }
+                unsigned                    order,
+                osg::Image*                 image,
+                const osgTerrain::Locator*  locator,
+                int                         lod,
+                const osgEarth::TileKey&    tileKey,
+                bool                        fallbackData =false )
+                  : _layer(imageLayer), _order(order), _locator(locator), _image(image),  _tileKey(tileKey), _lod(lod), _fallbackData(fallbackData) { }
 
             ColorData( const ColorData& rhs ) :
-                _layer( rhs._layer.get() ),        
+                _layer( rhs._layer.get() ),
+                _order( rhs._order ),
                 _locator( rhs._locator.get() ),
                 _image( rhs._image.get() ),
                 _tileKey( rhs._tileKey ),
@@ -115,6 +118,10 @@ namespace osgEarth_engine_quadtree
                 return _layer->getUID();
             }
 
+            unsigned getOrder() const {
+                return _order;
+            }
+
             const osgTerrain::Locator* getLocator() const {
                 return _locator.get();
             }
@@ -154,6 +161,7 @@ namespace osgEarth_engine_quadtree
             osgEarth::TileKey                        _tileKey;
             int                                      _lod;
             bool                                     _fallbackData;
+            unsigned                                 _order;
         };
 
         class ColorDataRef : public osg::Referenced
@@ -171,6 +179,7 @@ namespace osgEarth_engine_quadtree
         TileModel() { }
         virtual ~TileModel() { }
 
+        osg::ref_ptr<const Map>     _map; // observer_ptr?
         TileKey                     _tileKey;
         osg::ref_ptr<GeoLocator>    _tileLocator;
         ColorDataByUID              _colorData;
@@ -189,6 +198,6 @@ namespace osgEarth_engine_quadtree
         }
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL
+#endif // OSGEARTH_ENGINE_MP_TILE_MODEL
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler b/src/osgEarthDrivers/engine_mp/TileModelCompiler
similarity index 83%
copy from src/osgEarthDrivers/engine_quadtree/TileModelCompiler
copy to src/osgEarthDrivers/engine_mp/TileModelCompiler
index ba5b1ef..4e7a8dc 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
+++ b/src/osgEarthDrivers/engine_mp/TileModelCompiler
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,16 +16,14 @@
 * 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_TILE_MODEL_COMPILER
-#define OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_COMPILER 1
+#ifndef OSGEARTH_ENGINE_MP_TILE_MODEL_COMPILER
+#define OSGEARTH_ENGINE_MP_TILE_MODEL_COMPILER 1
 
 #include "Common"
 #include "TileModel"
-#include "QuadTreeTerrainEngineOptions"
+#include "MPTerrainEngineOptions"
 
 #include <osgEarth/Map>
-#include <osgEarth/TextureCompositor>
-#include <osgEarth/ThreadingUtils>
 #include <osgEarth/Locators>
 
 #include <osg/Node>
@@ -33,7 +31,7 @@
 #include <osg/Drawable>
 #include <osg/Array>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     using namespace osgEarth;
     using namespace osgEarth::Drivers;
@@ -77,9 +75,9 @@ namespace osgEarth_engine_quadtree
     public:
         TileModelCompiler(
             const MaskLayerVector&              masks,
-            TextureCompositor*                  compositor,
+            int                                 textureImageUnit,
             bool                                optimizeTriangleOrientation,
-            const QuadTreeTerrainEngineOptions& options);
+            const MPTerrainEngineOptions& options);
 
         /**
          * Compiles a tile model into an OSG scene graph. The scene graph will
@@ -92,13 +90,13 @@ namespace osgEarth_engine_quadtree
 
     protected:
         const MaskLayerVector&                    _masks;
-        osg::ref_ptr<TextureCompositor>           _texCompositor;
+        int                                       _textureImageUnit;
         bool                                      _optimizeTriOrientation;
-        const QuadTreeTerrainEngineOptions&       _options;
+        const MPTerrainEngineOptions&       _options;
         osg::ref_ptr<osg::Drawable::CullCallback> _cullByTraversalMask;
         CompilerCache                             _cache;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_COMPILER
+#endif // OSGEARTH_ENGINE_MP_TILE_MODEL_COMPILER
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp b/src/osgEarthDrivers/engine_mp/TileModelCompiler.cpp
similarity index 79%
copy from src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
copy to src/osgEarthDrivers/engine_mp/TileModelCompiler.cpp
index 86221d4..a4af319 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
+++ b/src/osgEarthDrivers/engine_mp/TileModelCompiler.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -17,24 +17,31 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include "TileModelCompiler"
+#include "MPGeometry"
 
 #include <osgEarth/Locators>
-#include <osgEarth/TextureCompositor>
+#include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
+#include <osgEarth/MapFrame>
+#include <osgEarth/ImageUtils>
 #include <osgEarthSymbology/Geometry>
 #include <osgEarthSymbology/MeshConsolidator>
 
 #include <osg/Geode>
 #include <osg/Geometry>
 #include <osg/MatrixTransform>
+#include <osg/GL2Extensions>
 #include <osgUtil/DelaunayTriangulator>
+#include <osgUtil/Optimizer>
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 using namespace osgEarth::Symbology;
 
 #define LC "[TileModelCompiler] "
 
+
 //------------------------------------------------------------------------
 
 osg::ref_ptr<osg::Vec2Array>&
@@ -88,10 +95,10 @@ namespace
     {
         osg::ref_ptr<osg::Vec3dArray> _boundary;
         osg::Vec3d                    _ndcMin, _ndcMax;
-        osg::Geometry*                _geom;
+        MPGeometry*                   _geom;
         osg::ref_ptr<osg::Vec3Array>  _internal;
 
-        MaskRecord(osg::Vec3dArray* boundary, osg::Vec3d& ndcMin, osg::Vec3d& ndcMax, osg::Geometry* geom) 
+        MaskRecord(osg::Vec3dArray* boundary, osg::Vec3d& ndcMin, osg::Vec3d& ndcMax, MPGeometry* geom) 
             : _boundary(boundary), _ndcMin(ndcMin), _ndcMax(ndcMax), _geom(geom), _internal(new osg::Vec3Array()) { }
     };
 
@@ -109,20 +116,20 @@ namespace
         {
             surfaceGeode     = 0L;
             surface          = 0L;
-            skirtGeode       = 0L;
             skirt            = 0L;
             stitching_skirts = 0L;
             ss_verts         = 0L;
-            //skirtHeight      = 0.0f;
             scaleHeight      = 1.0f;
             createSkirt      = false;
             i_sampleFactor   = 1.0f;
             j_sampleFactor   = 1.0f;
-            unifiedSkirtTexCoords       = 0L;
-            unifiedStitchSkirtTexCoords = 0L;
-            unifiedSurfaceTexCoords     = 0L;
+            useVBOs = !Registry::capabilities().preferDisplayListsForStaticGeometry();
+            textureImageUnit = 0;
         }
 
+        bool                     useVBOs;
+        int                      textureImageUnit;
+
         const TileModel*         model;                         // the tile's data model
         const MaskLayerVector&   maskLayers;                    // map-global masking layer set
         osg::ref_ptr<GeoLocator> geoLocator;                    // tile locator adjusted to geocentric
@@ -132,22 +139,18 @@ namespace
 
         // surface data:
         osg::Geode*                   surfaceGeode;
-        osg::Geometry*                surface;
+        MPGeometry*                   surface;
         osg::Vec3Array*               surfaceVerts;
         osg::Vec3Array*               normals;
         osg::Vec4Array*               surfaceElevData;
         unsigned                      numVerticesInSurface;
-        osg::Vec2Array*               unifiedSurfaceTexCoords;
         osg::ref_ptr<osg::FloatArray> elevations;
         Indices                       indices;
         osg::BoundingSphere           surfaceBound;
 
         // skirt data:
-        osg::Geode*              skirtGeode;
-        osg::Geometry*           skirt;
+        MPGeometry*              skirt;
         unsigned                 numVerticesInSkirt;
-        osg::Vec2Array*          unifiedSkirtTexCoords;
-        //double                   skirtHeight;
         bool                     createSkirt;
 
         // sampling grid parameters:
@@ -156,12 +159,13 @@ namespace
         double                   i_sampleFactor;
         double                   j_sampleFactor;
         double                   scaleHeight;
-
+        unsigned                 originalNumRows;
+        unsigned                 originalNumCols;
+        
         // for masking/stitching:
         MaskRecordVector         maskRecords;
-        osg::Geometry*           stitching_skirts;
+        MPGeometry*              stitching_skirts;
         osg::Vec3Array*          ss_verts;
-        osg::Vec2Array*          unifiedStitchSkirtTexCoords;
     };
 
 
@@ -226,8 +230,9 @@ namespace
 
               if (x_match && y_match)
               {
-                osg::Geometry* mask_geom = new osg::Geometry();
-                mask_geom->setUseVertexBufferObjects(true);
+                //osg::Geometry* mask_geom = new osg::Geometry();
+                MPGeometry* mask_geom = new MPGeometry( d.model->_map.get(), d.textureImageUnit );
+                mask_geom->setUseVertexBufferObjects(d.useVBOs);
                 d.surfaceGeode->addDrawable(mask_geom);
                 d.maskRecords.push_back( MaskRecord(boundary, min_ndc, max_ndc, mask_geom) );
               }
@@ -236,8 +241,9 @@ namespace
 
         if (d.maskRecords.size() > 0)
         {
-          d.stitching_skirts = new osg::Geometry();
-          d.stitching_skirts->setUseVertexBufferObjects(true);
+          //d.stitching_skirts = new osg::Geometry();
+          d.stitching_skirts = new MPGeometry( d.model->_map.get(), d.textureImageUnit );
+          d.stitching_skirts->setUseVertexBufferObjects(d.useVBOs);
           d.surfaceGeode->addDrawable( d.stitching_skirts );
 
           d.ss_verts = new osg::Vec3Array();
@@ -253,17 +259,21 @@ namespace
      * Calculates the sample rate and allocates all the vertex, normal, and color
      * arrays for the tile.
      */
-    void setupGeometryAttributes( Data& d, double sampleRatio )
+    void setupGeometryAttributes( Data& d, double sampleRatio, const osg::Vec4& color )
     {
         d.numRows = 8;
         d.numCols = 8;
+        d.originalNumRows = 8;
+        d.originalNumCols = 8;        
 
         // read the row/column count and skirt size from the model:
         osgTerrain::HeightFieldLayer* hflayer = d.model->_elevationData.getHFLayer();
         if (hflayer)
         {
             d.numCols = hflayer->getNumColumns();
-            d.numRows = hflayer->getNumRows();
+            d.numRows = hflayer->getNumRows();         
+            d.originalNumCols = d.numCols;
+            d.originalNumRows = d.numRows;
         }
 
         // calculate the elevation sampling factors that we'll use to step though
@@ -272,15 +282,12 @@ namespace
         d.j_sampleFactor = 1.0f;
 
         if ( sampleRatio != 1.0f )
-        {
-            unsigned originalNumCols = d.numCols;
-            unsigned originalNumRows = d.numRows;
-
-            d.numCols = osg::maximum((unsigned int) (float(originalNumCols)*sqrtf(sampleRatio)), 4u);
-            d.numRows = osg::maximum((unsigned int) (float(originalNumRows)*sqrtf(sampleRatio)), 4u);
+        {            
+            d.numCols = osg::maximum((unsigned int) (float(d.originalNumCols)*sqrtf(sampleRatio)), 4u);
+            d.numRows = osg::maximum((unsigned int) (float(d.originalNumRows)*sqrtf(sampleRatio)), 4u);
 
-            d.i_sampleFactor = double(originalNumCols-1)/double(d.numCols-1);
-            d.j_sampleFactor = double(originalNumRows-1)/double(d.numRows-1);
+            d.i_sampleFactor = double(d.originalNumCols-1)/double(d.numCols-1);
+            d.j_sampleFactor = double(d.originalNumRows-1)/double(d.numRows-1);
         }
 
 
@@ -304,7 +311,7 @@ namespace
 
         // allocate and assign color
         osg::Vec4Array* colors = new osg::Vec4Array(1);
-        (*colors)[0].set(1.0f,1.0f,1.0f,1.0f);
+        (*colors)[0] = color;
         d.surface->setColorArray( colors );
         d.surface->setColorBinding( osg::Geometry::BIND_OVERALL );
 
@@ -324,172 +331,95 @@ namespace
 
 
     /**
-     * Collects a list of color layers to render and assigns each one a texture image
-     * unit and a texture coordinate array. Color layers with the same locator setup
-     * will share a texture coordinate array.
-     *
-     * TODO: introduce support in the TextureCompositor to not only share the texture
-     * coordinate array, but also the binding attribute slot.
+     * Generates the texture coordinate arrays for each layer.
      */
-    void setupTextureAttributes( Data& d, TextureCompositor* compositor, CompilerCache& cache )
+    void setupTextureAttributes( Data& d, CompilerCache& cache )
     {
-        if ( compositor->requiresUnitTextureSpace() )
-        {
-            // for a unified unit texture space, just make a single texture coordinate array.
-            // this happens (for example) for a texture-array compositor where all textures
-            // share the same unit texture coordinate space.
-
-            d.unifiedSurfaceTexCoords = new osg::Vec2Array();
-            d.unifiedSurfaceTexCoords->reserve( d.numVerticesInSurface );
-            d.surface->setTexCoordArray( 0, d.unifiedSurfaceTexCoords );
-
-            if ( d.createSkirt )
-            {
-                d.unifiedSkirtTexCoords = new osg::Vec2Array();
-                d.unifiedSkirtTexCoords->reserve( d.numVerticesInSkirt );
-                d.skirt->setTexCoordArray( 0, d.unifiedSkirtTexCoords );
-            }
-
-            if (d.maskRecords.size() > 0)
-            {
-                d.unifiedStitchSkirtTexCoords = new osg::Vec2Array();
-                //unifiedStitchSkirtTexCoords->reserve( ? );
-                d.stitching_skirts->setTexCoordArray( 0, d.unifiedStitchSkirtTexCoords );
-            }
-        }
+        // Any color entries that have the same Locator will share a texcoord
+        // array, saving on memory.
+        d.renderLayers.reserve( d.model->_colorData.size() );
 
-        else // if ( !_texCompositor->requiresUnitTextureSpace() )
+        // build a list of "render layers", in rendering order, sharing texture coordinate
+        // arrays wherever possible.
+        for( TileModel::ColorDataByUID::const_iterator i = d.model->_colorData.begin(); i != d.model->_colorData.end(); ++i )
         {
-            // normal texture coordinate sharing. Any color entries that have the same
-            // color Locator will share a texcoord array, saving on memory.
-
-            // TODO: not only should we also share the vertex attribute slot (as mentioned
-            // above) but we should also share the texture coord arrays across ALL tiles,
-            // not just within a single tile. Perhaps a TerrainModel-wide cache.
-
-            //LocatorToTexCoordTable locatorToTexCoordTable;
-            //TileModelCompiler::LocatorToTexCoordTable texCoordArrayCache;
-
-            d.renderLayers.reserve( d.model->_colorData.size() );
+            const TileModel::ColorData& colorLayer = i->second;
+            RenderLayer r;
+            r._layer = colorLayer;
 
-            // build a list of "render layers", in rendering order, sharing texture coordinate
-            // arrays wherever possible.
-            for( TileModel::ColorDataByUID::const_iterator i = d.model->_colorData.begin(); i != d.model->_colorData.end(); ++i )
+            const GeoLocator* locator = dynamic_cast<const GeoLocator*>( r._layer.getLocator() );
+            if ( locator )
             {
-                const TileModel::ColorData& colorLayer = i->second;
-                RenderLayer r;
-                r._layer = colorLayer;
-
-                const GeoLocator* locator = dynamic_cast<const GeoLocator*>( r._layer.getLocator() );
-                if ( locator )
+                // if we have no mask records, we can use the texture coordinate array cache.
+                if ( d.maskRecords.size() == 0 )
                 {
-                    // if we have no mask records, we can use the texture coordinate array cache.
-                    if ( d.maskRecords.size() == 0 )
-                    {
-                        const GeoExtent& locex = locator->getDataExtent();
-                        const GeoExtent& keyex = d.model->_tileKey.getExtent();
+                    const GeoExtent& locex = locator->getDataExtent();
+                    const GeoExtent& keyex = d.model->_tileKey.getExtent();
 
-                        osg::Vec4d mat;
-                        mat[0] = (keyex.xMin() - locex.xMin())/locex.width();
-                        mat[1] = (keyex.yMin() - locex.yMin())/locex.height();
-                        mat[2] = (keyex.width() / locex.width());
-                        mat[3] = (keyex.height() / locex.height());
+                    osg::Vec4d mat;
+                    mat[0] = (keyex.xMin() - locex.xMin())/locex.width();
+                    mat[1] = (keyex.yMin() - locex.yMin())/locex.height();
+                    mat[2] = (keyex.width() / locex.width());
+                    mat[3] = (keyex.height() / locex.height());
 
-                        //OE_DEBUG << "key=" << d.model->_tileKey.str() << ": off=[" <<mat[0]<< ", " <<mat[1] << "] scale=["
-                        //    << mat[2]<< ", " << mat[3] << "]" << std::endl;
-
-                        osg::ref_ptr<osg::Vec2Array>& surfaceTexCoords = cache._surfaceTexCoordArrays.get( mat, d.numCols, d.numRows );
-                        if ( !surfaceTexCoords.valid() )
-                        {
-                            // Note: anything in the cache must have its own VBO. No sharing!
-                            surfaceTexCoords = new osg::Vec2Array();
-                            surfaceTexCoords->setVertexBufferObject( new osg::VertexBufferObject() );
-                            surfaceTexCoords->reserve( d.numVerticesInSurface );
-                            r._ownsTexCoords = true;
-                        }
-                        r._texCoords = surfaceTexCoords.get();
-
-                        osg::ref_ptr<osg::Vec2Array>& skirtTexCoords = cache._skirtTexCoordArrays.get( mat, d.numCols, d.numRows );
-                        if ( !skirtTexCoords.valid() )
-                        {
-                            // Note: anything in the cache must have its own VBO. No sharing!
-                            skirtTexCoords = new osg::Vec2Array();
-                            skirtTexCoords->setVertexBufferObject( new osg::VertexBufferObject() );
-                            skirtTexCoords->reserve( d.numVerticesInSkirt );
-                            r._ownsSkirtTexCoords = true;
-                        }
-                        r._skirtTexCoords = skirtTexCoords.get();
-                    }
+                    //OE_DEBUG << "key=" << d.model->_tileKey.str() << ": off=[" <<mat[0]<< ", " <<mat[1] << "] scale=["
+                    //    << mat[2]<< ", " << mat[3] << "]" << std::endl;
 
-                    else // if ( d.maskRecords.size() > 0 )
+                    osg::ref_ptr<osg::Vec2Array>& surfaceTexCoords = cache._surfaceTexCoordArrays.get( mat, d.numCols, d.numRows );
+                    if ( !surfaceTexCoords.valid() )
                     {
-                        // cannot use the tex coord array cache if there are masking records.
-                        r._texCoords = new osg::Vec2Array();
-                        r._texCoords->reserve( d.numVerticesInSurface );
+                        // Note: anything in the cache must have its own VBO. No sharing!
+                        surfaceTexCoords = new osg::Vec2Array();
+                        surfaceTexCoords->setVertexBufferObject( new osg::VertexBufferObject() );
+                        surfaceTexCoords->reserve( d.numVerticesInSurface );
                         r._ownsTexCoords = true;
-
-                        r._skirtTexCoords = new osg::Vec2Array();
-                        r._skirtTexCoords->reserve( d.numVerticesInSkirt );
-                        r._ownsSkirtTexCoords = true;
-                    
-                        r._stitchTexCoords = new osg::Vec2Array();
-                        r._stitchSkirtTexCoords = new osg::Vec2Array();
                     }
+                    r._texCoords = surfaceTexCoords.get();
 
-                    r._locator = locator;
-                    if ( locator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC )
+                    osg::ref_ptr<osg::Vec2Array>& skirtTexCoords = cache._skirtTexCoordArrays.get( mat, d.numCols, d.numRows );
+                    if ( !skirtTexCoords.valid() )
                     {
-                        const GeoLocator* geo = dynamic_cast<const GeoLocator*>(locator);
-                        if ( geo )
-                            r._locator = geo->getGeographicFromGeocentric();
+                        // Note: anything in the cache must have its own VBO. No sharing!
+                        skirtTexCoords = new osg::Vec2Array();
+                        skirtTexCoords->setVertexBufferObject( new osg::VertexBufferObject() );
+                        skirtTexCoords->reserve( d.numVerticesInSkirt );
+                        r._ownsSkirtTexCoords = true;
                     }
+                    r._skirtTexCoords = skirtTexCoords.get();
+                }
 
-                    d.renderLayers.push_back( r );
+                else // if ( d.maskRecords.size() > 0 )
+                {
+                    // cannot use the tex coord array cache if there are masking records.
+                    r._texCoords = new osg::Vec2Array();
+                    r._texCoords->reserve( d.numVerticesInSurface );
+                    r._ownsTexCoords = true;
+
+                    r._skirtTexCoords = new osg::Vec2Array();
+                    r._skirtTexCoords->reserve( d.numVerticesInSkirt );
+                    r._ownsSkirtTexCoords = true;
 
-                    // Note that we don't actually assign the tex coord arrays to the geometry yet.
-                    // That must wait until the end. See the comments in assignTextureArrays()
-                    // to understand why.
+                    r._stitchTexCoords = new osg::Vec2Array();
+                    r._stitchSkirtTexCoords = new osg::Vec2Array();
                 }
-                else
+
+                r._locator = locator;
+                if ( locator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC )
                 {
-                    OE_WARN << LC << "Found a Locator, but it wasn't a GeoLocator." << std::endl;
+                    const GeoLocator* geo = dynamic_cast<const GeoLocator*>(locator);
+                    if ( geo )
+                        r._locator = geo->getGeographicFromGeocentric();
                 }
-            }
-        }
-    }
 
+                d.renderLayers.push_back( r );
 
-    /**
-     * Assigns any texture coordinate arrays to the corresponding geometry.
-     *
-     * NOTE: This has to happen AFTER all other arrays have been assigned to the geometry.
-     * Here is why:
-     *
-     * This compiler attempts to share texture coordinate arrays between tiles where possible.
-     * When you call Geometry::setVertexArray (or other similar functions) OSG attempts to find
-     * an existing VBO that it can use to attach to the array. If a shared texture coordinate
-     * array is in the Geometry, and OSG will find its VBO and attempt to use it so store the
-     * buffer data for the new array. In doing do it will be modifying a VBO that is potentially
-     * in use by another thread, causing a crash.
-     *
-     * By assigning the shared arrays last, we prevent this from happening.
-     */
-    void assignTextureArrays( Data& d, TextureCompositor* compositor )
-    {
-        for( RenderLayerVector::const_iterator r = d.renderLayers.begin(); r != d.renderLayers.end(); ++r )
-        {
-            compositor->assignTexCoordArray( d.surface, r->_layer.getUID(), r->_texCoords.get() );
-            compositor->assignTexCoordArray( d.skirt,   r->_layer.getUID(), r->_skirtTexCoords.get() );
-
-            // If we have mask stitching geometries, those need tex coords too:
-            for ( MaskRecordVector::iterator mr = d.maskRecords.begin(); mr != d.maskRecords.end(); ++mr )
-            {
-                compositor->assignTexCoordArray( (*mr)._geom, r->_layer.getUID(), r->_stitchTexCoords.get() );
+                // Note that we don't actually assign the tex coord arrays to the geometry yet.
+                // That must wait until the end. See the comments in assignTextureArrays()
+                // to understand why.
             }
-
-            if (d.stitching_skirts)
+            else
             {
-                compositor->assignTexCoordArray( d.stitching_skirts, r->_layer.getUID(), r->_stitchSkirtTexCoords.get() );
+                OE_WARN << LC << "Found a Locator, but it wasn't a GeoLocator." << std::endl;
             }
         }
     }
@@ -499,7 +429,7 @@ namespace
      * Iterate over the sampling grid and calculate the vertex positions and normals
      * for each sampling point.
      */
-    void createSurfaceGeometry( Data& d, TextureCompositor* compositor )
+    void createSurfaceGeometry( Data& d )
     {
         d.surfaceBound.init();
 
@@ -559,29 +489,20 @@ namespace
                     // grow the bounding sphere:
                     d.surfaceBound.expandBy( (*d.surfaceVerts).back() );
 
-
-                    if ( compositor->requiresUnitTextureSpace() )
-                    {
-                        // the unified unit texture space requires a single, untransformed unit coord [0..1]
-                        (*d.unifiedSurfaceTexCoords).push_back( osg::Vec2( ndc.x(), ndc.y() ) );
-                    }
-                    else
+                    // the separate texture space requires separate transformed texcoords for each layer.
+                    for( RenderLayerVector::const_iterator r = d.renderLayers.begin(); r != d.renderLayers.end(); ++r )
                     {
-                        // the separate texture space requires separate transformed texcoords for each layer.
-                        for( RenderLayerVector::const_iterator r = d.renderLayers.begin(); r != d.renderLayers.end(); ++r )
+                        if ( r->_ownsTexCoords )
                         {
-                            if ( r->_ownsTexCoords )
+                            if ( !r->_locator->isEquivalentTo( *d.geoLocator.get() ) )
                             {
-                                if ( !r->_locator->isEquivalentTo( *d.geoLocator.get() ) )
-                                {
-                                    osg::Vec3d color_ndc;
-                                    osgTerrain::Locator::convertLocalCoordBetween( *d.geoLocator.get(), ndc, *r->_locator.get(), color_ndc );
-                                    r->_texCoords->push_back( osg::Vec2( color_ndc.x(), color_ndc.y() ) );
-                                }
-                                else
-                                {
-                                    r->_texCoords->push_back( osg::Vec2( ndc.x(), ndc.y() ) );
-                                }
+                                osg::Vec3d color_ndc;
+                                osgTerrain::Locator::convertLocalCoordBetween( *d.geoLocator.get(), ndc, *r->_locator.get(), color_ndc );
+                                r->_texCoords->push_back( osg::Vec2( color_ndc.x(), color_ndc.y() ) );
+                            }
+                            else
+                            {
+                                r->_texCoords->push_back( osg::Vec2( ndc.x(), ndc.y() ) );
                             }
                         }
                     }
@@ -592,12 +513,10 @@ namespace
                     // compute the local normal
                     osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0;
                     osg::Vec3d model_one;
-                    //_masterLocator->convertLocalToModel(ndc_one, model_one);
                     d.model->_tileLocator->unitToModel(ndc_one, model_one);
                     model_one = model_one - model;
                     model_one.normalize();    
 
-                    //(*normals)[k] = model_one;
                     (*d.normals).push_back(model_one);
 
                     // store the unit extrusion vector and the raw height value.
@@ -613,7 +532,7 @@ namespace
      * and the internal verticies to populate it. Then build a triangulation of the
      * area inside the masking bounding box and add this to the surface geode.
      */
-    void createMaskGeometry( Data& d, TextureCompositor* compositor )
+    void createMaskGeometry( Data& d )
     {
         osgTerrain::HeightFieldLayer* elevationLayer = d.model->_elevationData.getHFLayer();
 
@@ -898,14 +817,7 @@ namespace
 
 
                 //Initialize tex coords
-                osg::Vec2Array* unifiedStitchTexCoords = 0L;
-                if (compositor->requiresUnitTextureSpace())
-                {
-                    unifiedStitchTexCoords = new osg::Vec2Array();
-                    unifiedStitchTexCoords->reserve(trig->getInputPointArray()->size());
-                    stitch_geom->setTexCoordArray(0, unifiedStitchTexCoords);
-                }
-                else if ( d.renderLayers.size() > 0 )
+                if ( d.renderLayers.size() > 0 )
                 {
                     for (unsigned int i = 0; i < d.renderLayers.size(); ++i)
                     {
@@ -934,11 +846,7 @@ namespace
                     (*stitch_norms)[++norm_i] = model_one;
 
                     // set up text coords
-                    if (compositor->requiresUnitTextureSpace())
-                    {
-                        unifiedStitchTexCoords->push_back(osg::Vec2((*it).x(), (*it).y()));
-                    }
-                    else if (d.renderLayers.size() > 0)
+                    if (d.renderLayers.size() > 0)
                     {
                         for (unsigned int i = 0; i < d.renderLayers.size(); ++i)
                         {
@@ -970,7 +878,7 @@ namespace
      * tile edges that hides the gap effect caused when you render two adjacent tiles at
      * different LODs.
      */
-    void createSkirtGeometry( Data& d, TextureCompositor* compositor, double skirtRatio )
+    void createSkirtGeometry( Data& d, double skirtRatio )
     {
         // surface normals will double as our skirt extrusion vectors
         osg::Vec3Array* skirtVectors = d.normals;
@@ -1015,12 +923,7 @@ namespace
                 skirtElevData->push_back( elevData );
                 skirtElevData->push_back( elevData - osg::Vec4f(0,0,0,skirtHeight) );
 
-                if ( compositor->requiresUnitTextureSpace() )
-                {
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                }
-                else if ( d.renderLayers.size() > 0 )
+                if ( d.renderLayers.size() > 0 )
                 {
                     for (unsigned int i = 0; i < d.renderLayers.size(); ++i)
                     {
@@ -1058,12 +961,7 @@ namespace
                 skirtElevData->push_back( elevData );
                 skirtElevData->push_back( elevData - osg::Vec4f(0,0,0,skirtHeight) );
 
-                if ( compositor->requiresUnitTextureSpace() )
-                {
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                }
-                else if ( d.renderLayers.size() > 0 )
+                if ( d.renderLayers.size() > 0 )
                 {
                     for (unsigned int i = 0; i < d.renderLayers.size(); ++i)
                     {
@@ -1101,12 +999,7 @@ namespace
                 skirtElevData->push_back( elevData );
                 skirtElevData->push_back( elevData - osg::Vec4f(0,0,0,skirtHeight) );
 
-                if ( compositor->requiresUnitTextureSpace() )
-                {
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                }
-                else if ( d.renderLayers.size() > 0 )
+                if ( d.renderLayers.size() > 0 )
                 {
                     for (unsigned int i = 0; i < d.renderLayers.size(); ++i)
                     {
@@ -1144,12 +1037,7 @@ namespace
                 skirtElevData->push_back( elevData );
                 skirtElevData->push_back( elevData - osg::Vec4f(0,0,0,skirtHeight) );
 
-                if ( compositor->requiresUnitTextureSpace() )
-                {
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                    d.unifiedSkirtTexCoords->push_back( (*d.unifiedSurfaceTexCoords)[orig_i] );
-                }
-                else if ( d.renderLayers.size() > 0 )
+                if ( d.renderLayers.size() > 0 )
                 {
                     for (unsigned int i = 0; i < d.renderLayers.size(); ++i)
                     {
@@ -1171,6 +1059,12 @@ namespace
         d.skirt->setNormalArray( skirtNormals );
         d.skirt->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
 
+        if ( d.surface->getColorArray() )
+        {
+            d.skirt->setColorArray( d.surface->getColorArray() );
+            d.skirt->setColorBinding( osg::Geometry::BIND_OVERALL );
+        }
+
         d.skirt->setVertexAttribArray    (osg::Drawable::ATTRIBUTE_6, skirtElevData );
         d.skirt->setVertexAttribBinding  (osg::Drawable::ATTRIBUTE_6, osg::Geometry::BIND_PER_VERTEX);
         d.skirt->setVertexAttribNormalize(osg::Drawable::ATTRIBUTE_6, false);
@@ -1207,7 +1101,6 @@ namespace
         else
             elements = new osg::DrawElementsUShort(GL_TRIANGLES);
 
-        //DrawElementsUShort* elements = new osg::DrawElementsUShort(GL_TRIANGLES);
         elements->reserveElements((d.numRows-1) * (d.numCols-1) * 6);
         d.surface->addPrimitiveSet( elements );
 
@@ -1330,28 +1223,27 @@ namespace
                     }
                 }
             }
-        }
-
+        }        
         
         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 );            
-
+            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)
+            if (w_neighbor.valid() && w_neighbor->getNumColumns() == d.originalNumCols && w_neighbor->getNumRows() == d.originalNumRows)            
             {                                     
                 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++)
                 {
@@ -1359,8 +1251,12 @@ namespace
                     {                          
                         osg::Vec3d ndc( (double)(i - static_cast<int>(d.numCols-1))/(double)(d.numCols-1), ((double)j)/(double)(d.numRows-1), 0.0);                                                                        
 
+                        // use the sampling factor to determine the lookup index:
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
+
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = w_neighbor->getHeight( i, j );
+                        float heightValue = w_neighbor->getHeight( i_equiv, j_equiv );
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1429,7 +1325,7 @@ namespace
 
                         
             //Recalculate the east side
-            if (e_neighbor.valid() && e_neighbor->getNumColumns() == d.numCols && e_neighbor->getNumRows() == d.numRows)
+            if (e_neighbor.valid() && e_neighbor->getNumColumns() == d.originalNumCols && e_neighbor->getNumRows() == d.originalNumRows)            
             {                           
                 osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
                 boundaryVerts->reserve( 2 * d.numRows );
@@ -1443,9 +1339,12 @@ namespace
                     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);
+
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
                         
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = e_neighbor->getHeight( i, j );
+                        float heightValue = e_neighbor->getHeight( i_equiv, j_equiv );
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1511,7 +1410,7 @@ namespace
             }
 
             //Recalculate the north side
-            if (n_neighbor.valid() && n_neighbor->getNumColumns() == d.numCols && n_neighbor->getNumRows() == d.numRows)
+            if (n_neighbor.valid() && n_neighbor->getNumColumns() == d.originalNumCols && n_neighbor->getNumRows() == d.originalNumRows)            
             {                 
                 osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
                 boundaryVerts->reserve( 2 * d.numCols );
@@ -1525,10 +1424,12 @@ namespace
                     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);                        
+
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
                         
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = n_neighbor->getHeight( i, j );
+                        float heightValue = n_neighbor->getHeight( i_equiv, j_equiv );
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1594,7 +1495,7 @@ namespace
             }
 
             //Recalculate the south side
-            if (s_neighbor.valid() && s_neighbor->getNumColumns() == d.numCols && s_neighbor->getNumRows() == d.numRows)
+            if (s_neighbor.valid() && s_neighbor->getNumColumns() == d.originalNumCols && s_neighbor->getNumRows() == d.originalNumRows)            
             {                
                 osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
                 boundaryVerts->reserve( 2 * d.numCols );
@@ -1608,9 +1509,12 @@ namespace
                     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);                                                
+
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
                         
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = s_neighbor->getHeight( i, j );
+                        float heightValue = s_neighbor->getHeight( i_equiv, j_equiv );                        
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1688,55 +1592,70 @@ namespace
     }
 
 
-    /**
-     * Builds a state set for the tile's Geodes that encodes the color layers as textures
-     * according to the setup of the compistor.
-     */
-    osg::StateSet* createStateSet( Data& d, TextureCompositor* compositor )
+    void installRenderData( Data& d )
     {
-        const GeoExtent& tileExtent = d.geoLocator->getDataExtent();
-
-        osg::StateSet* stateSet = new osg::StateSet();
-
-        // mark as DYNAMIC so we can access it from different threads.
-        // TODO: need thing??
-        // stateSet->setDataVariance( osg::Object::DYNAMIC );
-
-        //TODO: implement this to support blending.
-        osg::StateSet* parentStateSet = d.model->_parentStateSet.get();
-
-        for( TileModel::ColorDataByUID::const_iterator i = d.model->_colorData.begin(); i != d.model->_colorData.end(); ++i )
+        // pre-size all vectors:
+        unsigned size = d.renderLayers.size();
+
+        d.surface->_layers.resize( size );
+        if ( d.skirt )
+            d.skirt->_layers.resize( size );
+        for ( MaskRecordVector::iterator mr = d.maskRecords.begin(); mr != d.maskRecords.end(); ++mr )
+            mr->_geom->_layers.resize( size );
+        if ( d.stitching_skirts )
+            d.stitching_skirts->_layers.resize( size );
+
+        // install the render data for each layer:
+        for( RenderLayerVector::const_iterator r = d.renderLayers.begin(); r != d.renderLayers.end(); ++r )
         {
-            const TileModel::ColorData& colorLayer = i->second;
-
-            bool isRealData = !colorLayer.isFallbackData();
-
-            osg::ref_ptr<const GeoLocator> layerGeoLocator = dynamic_cast<const GeoLocator*>( colorLayer.getLocator() );
-            if ( layerGeoLocator.valid() )
+            osg::Image* image = r->_layer.getImage();
+
+            osg::Texture2D* tex = new osg::Texture2D( image );
+            tex->setUnRefImageDataAfterApply( true );
+            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::MIN_FILTER, osg::Texture::LINEAR );
+            tex->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE );
+            tex->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
+            tex->setWrap( osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE );
+
+            unsigned order = r->_layer.getOrder();
+
+            MPGeometry::Layer layer;
+            layer._layerID        = r->_layer.getUID();
+            layer._imageLayer     = r->_layer.getMapLayer();
+            layer._tex            = tex;
+
+            // the surface:
+            layer._texCoords  = r->_texCoords;
+            d.surface->_layers[order] = layer;
+
+            // the skirt:
+            if ( d.skirt )
             {
-                if ( layerGeoLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC )
-                    layerGeoLocator = layerGeoLocator->getGeographicFromGeocentric();
-
-                GeoImage sourceImage( colorLayer.getImage(), layerGeoLocator->getDataExtent() );
+                layer._texCoords  = r->_skirtTexCoords;
+                d.skirt->_layers[order] = layer;
+            }
 
-                GeoImage preparedImage = compositor->prepareImage( 
-                    sourceImage, 
-                    tileExtent );
+            // the mask geometries:
+            for ( MaskRecordVector::iterator mr = d.maskRecords.begin(); mr != d.maskRecords.end(); ++mr )
+            {
+                layer._texCoords = r->_stitchTexCoords.get();
+                mr->_geom->_layers[order] = layer;
+            }
 
-                compositor->applyLayerUpdate( 
-                    stateSet, 
-                    colorLayer.getUID(), 
-                    preparedImage, 
-                    d.model->_tileKey,
-                    isRealData ? parentStateSet : 0L );
+            // the stitching skirts:
+            if ( d.stitching_skirts )
+            {
+                layer._texCoords = r->_stitchSkirtTexCoords.get();
+                d.stitching_skirts->_layers[order] = layer;
             }
         }
-
-        return stateSet;
     }
 
 
-
     struct CullByTraversalMask : public osg::Drawable::CullCallback
     {
         CullByTraversalMask( unsigned mask ) : _mask(mask) { }
@@ -1752,16 +1671,14 @@ namespace
 //------------------------------------------------------------------------
 
 TileModelCompiler::TileModelCompiler(const MaskLayerVector&              masks,
-                                     TextureCompositor*                  texCompositor,
+                                     int                                 texImageUnit,
                                      bool                                optimizeTriOrientation,
-                                     const QuadTreeTerrainEngineOptions& options) :
+                                     const MPTerrainEngineOptions& options) :
 _masks                 ( masks ),
-_texCompositor         ( texCompositor ),
 _optimizeTriOrientation( optimizeTriOrientation ),
-_options               ( options )
+_options               ( options ),
+_textureImageUnit      ( texImageUnit )
 {
-    //nop
-
     _cullByTraversalMask = new CullByTraversalMask(*options.secondaryTraversalMask());
 }
 
@@ -1781,8 +1698,8 @@ TileModelCompiler::compile(const TileModel* model,
     osg::MatrixTransform* xform = new osg::MatrixTransform( osg::Matrix::translate(d.centerModel) );
 
     // A Geode/Geometry for the surface:
-    d.surface = new osg::Geometry();
-    d.surface->setUseVertexBufferObjects(true);
+    d.surface = new MPGeometry( model->_map.get(), _textureImageUnit );
+    d.surface->setUseVertexBufferObjects(d.useVBOs);
     d.surfaceGeode = new osg::Geode();
     d.surfaceGeode->addDrawable( d.surface );
     d.surfaceGeode->setNodeMask( *_options.primaryTraversalMask() );
@@ -1796,16 +1713,11 @@ TileModelCompiler::compile(const TileModel* model,
 
     if ( d.createSkirt )
     {
-        d.skirt = new osg::Geometry();
-        d.skirt->setUseVertexBufferObjects(true);
-
-        //d.skirtGeode = new osg::Geode();
-        //d.skirtGeode->addDrawable( d.skirt );
-        //d.skirtGeode->setNodeMask( *_options.secondaryTraversalMask() );
-        //xform->addChild( d.skirtGeode );
+        d.skirt = new MPGeometry( model->_map.get(), _textureImageUnit );
+        d.skirt->setUseVertexBufferObjects(d.useVBOs);
 
         // slightly faster than a separate geode:
-        d.skirt->setDataVariance( osg::Object::DYNAMIC ); // since we're using a custom cull callback
+        //d.skirt->setDataVariance( osg::Object::DYNAMIC ); // since we're using a custom cull callback
         d.skirt->setCullCallback( _cullByTraversalMask.get() );
         d.surfaceGeode->addDrawable( d.skirt );
     }
@@ -1826,38 +1738,29 @@ TileModelCompiler::compile(const TileModel* model,
     if ( sampleRatio <= 0.0f )
         sampleRatio = osg::clampBetween( model->_tileKey.getLevelOfDetail()/20.0, 0.0625, 1.0 );
 
-    setupGeometryAttributes( d, sampleRatio );
+    setupGeometryAttributes( d, sampleRatio, *_options.color() );
 
     // set up the list of layers to render and their shared arrays.
-    setupTextureAttributes( d, _texCompositor.get(), _cache );
+    setupTextureAttributes( d, _cache );
 
     // calculate the vertex and normals for the surface geometry.
-    createSurfaceGeometry( d, _texCompositor.get() );
+    createSurfaceGeometry( d );
 
     // build geometry for the masked areas, if applicable
     if ( d.maskRecords.size() > 0 )
-        createMaskGeometry( d, _texCompositor.get() );
+        createMaskGeometry( d );
 
     // build the skirts.
     if ( d.createSkirt )
-        createSkirtGeometry( d, _texCompositor.get(), *_options.heightFieldSkirtRatio() );
+        createSkirtGeometry( d, *_options.heightFieldSkirtRatio() );
 
     // tesselate the surface verts into triangles.
     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:
-    assignTextureArrays( d, _texCompositor.get() );
-
-    // create the StateSet that will active texture composition.
-    osg::StateSet* stateSet = createStateSet( d, _texCompositor.get() );
-
-    if ( stateSet )
-        xform->setStateSet( stateSet );
-
+    // installs the per-layer rendering data into the Geometry objects.
+    installRenderData( d );
 
     // lastly, optimize the results.
-
     // unnecessary (I think) since tessellateSurfaceGeometry already makes an optimal
     // triangle set
     //if ( d.maskRecords.size() > 0 )
@@ -1875,7 +1778,6 @@ TileModelCompiler::compile(const TileModel* model,
         MeshConsolidator::convertToTriangles( *((*mr)._geom) );
     }
     
-   
     if (osgDB::Registry::instance()->getBuildKdTreesHint()==osgDB::ReaderWriter::Options::BUILD_KDTREES &&
         osgDB::Registry::instance()->getKdTreeBuilder())
     {            
@@ -1884,6 +1786,6 @@ TileModelCompiler::compile(const TileModel* model,
     }
 
     out_node     = xform;
-    out_stateSet = stateSet;
+    out_stateSet = 0L;
     return true;
 }
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelFactory b/src/osgEarthDrivers/engine_mp/TileModelFactory
similarity index 84%
copy from src/osgEarthDrivers/engine_quadtree/TileModelFactory
copy to src/osgEarthDrivers/engine_mp/TileModelFactory
index 6dbe44c..46f0498 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelFactory
+++ b/src/osgEarthDrivers/engine_mp/TileModelFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,13 +16,13 @@
 * 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_TILE_MODEL_FACTORY
-#define OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_FACTORY 1
+#ifndef OSGEARTH_ENGINE_MP_TILE_MODEL_FACTORY
+#define OSGEARTH_ENGINE_MP_TILE_MODEL_FACTORY 1
 
 #include "Common"
 #include "TileNode"
 #include "TileNodeRegistry"
-#include "QuadTreeTerrainEngineOptions"
+#include "MPTerrainEngineOptions"
 #include <osgEarth/Map>
 #include <osgEarth/ThreadingUtils>
 #include <osgEarth/Containers>
@@ -31,7 +31,7 @@
 #include <osgEarth/MapInfo>
 #include <osg/Group>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     using namespace osgEarth;
 
@@ -64,12 +64,12 @@ namespace osgEarth_engine_quadtree
         }
 
         bool getOrCreateHeightField( 
-                const MapFrame&                       frame,  
+                const MapFrame&                 frame,
                 const TileKey&                  key,
                 bool                            fallback,
-                osg::ref_ptr<osg::HeightField>& out_hf,            
-                bool*                           out_isFallback =0L,   
-                bool                            convertToHAE   =true,         
+                osg::ref_ptr<osg::HeightField>& out_hf,
+                bool*                           out_isFallback =0L,
+                bool                            convertToHAE   =true,
                 ElevationSamplePolicy           samplePolicy   =SAMPLE_FIRST_VALID,
                 ProgressCallback*               progress       =0L ) const
         {                
@@ -79,16 +79,16 @@ namespace osgEarth_engine_quadtree
             cachekey._fallback     = fallback;
             cachekey._convertToHAE = convertToHAE;
             cachekey._samplePolicy = samplePolicy;
-            LRUCache<HFKey,HFValue>::Record rec = _cache.get( cachekey );        
 
             bool hit = false;
-            if ( rec.valid() )
+            LRUCache<HFKey,HFValue>::Record rec;
+            if ( _cache.get(cachekey, rec) )
             {
                 out_hf = rec.value()._hf.get();
                 if ( out_isFallback )
-                    *out_isFallback = rec.value()._isFallback;                        
-                return true;            
-            }                
+                    *out_isFallback = rec.value()._isFallback;
+                return true;
+            }
 
             bool isFallback;
 
@@ -140,7 +140,7 @@ namespace osgEarth_engine_quadtree
         TileModelFactory(
             const Map*                                   map,
             TileNodeRegistry*                            liveTiles,
-            const Drivers::QuadTreeTerrainEngineOptions& terrainOptions );
+            const Drivers::MPTerrainEngineOptions& terrainOptions );
 
         HeightFieldCache* getHeightFieldCache() const;
 
@@ -157,10 +157,10 @@ namespace osgEarth_engine_quadtree
 
         const Map*                                   _map;
         osg::ref_ptr<TileNodeRegistry>               _liveTiles;
-        const Drivers::QuadTreeTerrainEngineOptions& _terrainOptions;
+        const Drivers::MPTerrainEngineOptions& _terrainOptions;
         osg::ref_ptr< HeightFieldCache > _hfCache;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_TILE_MODEL_FACTORY
+#endif // OSGEARTH_ENGINE_MP_TILE_MODEL_FACTORY
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp b/src/osgEarthDrivers/engine_mp/TileModelFactory.cpp
similarity index 80%
copy from src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
copy to src/osgEarthDrivers/engine_mp/TileModelFactory.cpp
index ae2bdcf..ac83bc9 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
+++ b/src/osgEarthDrivers/engine_mp/TileModelFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -22,7 +22,7 @@
 #include <osgEarth/ImageUtils>
 #include <osgEarth/HeightFieldUtils>
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 using namespace OpenThreads;
@@ -37,19 +37,20 @@ namespace
     {
         void init( const TileKey&                      key, 
                    ImageLayer*                         layer, 
+                   unsigned                            order,
                    const MapInfo&                      mapInfo,
-                   const QuadTreeTerrainEngineOptions& opt, 
+                   const MPTerrainEngineOptions& opt, 
                    TileModel*                          model )
         {
             _key      = key;
             _layer    = layer;
+            _order    = order;
             _mapInfo  = &mapInfo;
             _opt      = &opt;
             _model    = model;
-            //_repo     = &repo;
         }
 
-        void execute()
+        bool execute()
         {
             GeoImage geoImage;
             bool isFallbackData = false;
@@ -107,38 +108,47 @@ namespace
                 }
             }
 
-            GeoLocator* locator = 0L;
-
-            if ( !geoImage.valid() )
-            {
-                // no image found, so make an empty one (one pixel alpha).
-                geoImage = GeoImage( ImageUtils::createEmptyImage(), _key.getExtent() );
-                locator = GeoLocator::createForKey( _key, *_mapInfo );
-                isFallbackData = true;
-            }
-            else
+            if ( geoImage.valid() )
             {
+                GeoLocator* locator = 0L;
+                
                 if ( useMercatorFastPath )
                     locator = new MercatorLocator(geoImage.getExtent());
                 else
                     locator = GeoLocator::createForExtent(geoImage.getExtent(), *_mapInfo);
-            }
 
-            // add the color layer to the repo.
-            _model->_colorData[_layer->getUID()] = TileModel::ColorData(
-                _layer,
-                geoImage.getImage(),
-                locator,
-                _key.getLevelOfDetail(),
-                _key,
-                isFallbackData );
+                // convert the image to PMA. This must be done in the CPU; for some
+                // reason (which we could not determine) it fails to try this after the
+                // texture lookup in the shader.
+                if ( _opt->premultipliedAlpha() == true )
+                {
+                    ImageUtils::convertToPremultipliedAlpha( geoImage.getImage() );
+                }
+
+                // add the color layer to the repo.
+                _model->_colorData[_layer->getUID()] = TileModel::ColorData(
+                    _layer,
+                    _order,
+                    geoImage.getImage(),
+                    locator,
+                    _key.getLevelOfDetail(),
+                    _key,
+                    isFallbackData );
+
+                return true;
+            }
+            else
+            {
+                return false;
+            }
         }
 
         TileKey        _key;
         const MapInfo* _mapInfo;
         ImageLayer*    _layer;
+        unsigned       _order;
         TileModel*     _model;
-        const QuadTreeTerrainEngineOptions* _opt;
+        const MPTerrainEngineOptions* _opt;
     };
 }
 
@@ -148,7 +158,7 @@ namespace
 {
     struct BuildElevationData
     {
-        void init(const TileKey& key, const MapFrame& mapf, const QuadTreeTerrainEngineOptions& opt, TileModel* model, HeightFieldCache* hfCache) //sourceTileNodeBuilder::SourceRepo& repo)
+        void init(const TileKey& key, const MapFrame& mapf, const MPTerrainEngineOptions& opt, TileModel* model, HeightFieldCache* hfCache) //sourceTileNodeBuilder::SourceRepo& repo)
         {
             _key   = key;
             _mapf  = &mapf;
@@ -211,7 +221,7 @@ namespace
 
         TileKey                  _key;
         const MapFrame*          _mapf;
-        const QuadTreeTerrainEngineOptions* _opt;
+        const MPTerrainEngineOptions* _opt;
         TileModel* _model;
         osg::ref_ptr< HeightFieldCache> _hfCache;
     };
@@ -221,7 +231,7 @@ namespace
 
 TileModelFactory::TileModelFactory(const Map*                          map, 
                                    TileNodeRegistry*                   liveTiles,
-                                   const QuadTreeTerrainEngineOptions& terrainOptions ) :
+                                   const MPTerrainEngineOptions& terrainOptions ) :
 _map           ( map ),
 _liveTiles     ( liveTiles ),
 _terrainOptions( terrainOptions )
@@ -247,7 +257,8 @@ TileModelFactory::createTileModel(const TileKey&           key,
     const MapInfo& mapInfo = mapf.getMapInfo();
 
     osg::ref_ptr<TileModel> model = new TileModel();
-    model->_tileKey = key;
+    model->_map         = _map;
+    model->_tileKey     = key;
     model->_tileLocator = GeoLocator::createForKey(key, mapInfo);
 
     // init this to false, then search for real data. "Real data" is data corresponding
@@ -257,6 +268,7 @@ TileModelFactory::createTileModel(const TileKey&           key,
     out_hasLodBlendedLayers = false;
     
     // Fetch the image data and make color layers.
+    unsigned order = 0;
     for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
     {
         ImageLayer* layer = i->get();
@@ -264,12 +276,18 @@ TileModelFactory::createTileModel(const TileKey&           key,
         if ( layer->getEnabled() )
         {
             BuildColorData build;
-            build.init( key, layer, mapInfo, _terrainOptions, model.get() );
-            build.execute();
-
-            if ( layer->getImageLayerOptions().lodBlending() == true )
+            build.init( key, layer, order, mapInfo, _terrainOptions, model.get() );
+            
+            bool addedToModel = build.execute();
+            if ( addedToModel )
             {
-                out_hasLodBlendedLayers = true;
+                if ( layer->getImageLayerOptions().lodBlending() == true )
+                {
+                    out_hasLodBlendedLayers = true;
+                }
+
+                // only bump the order if we added something to the data model.
+                order++;
             }
         }
     }
@@ -295,35 +313,6 @@ TileModelFactory::createTileModel(const TileKey&           key,
         model->_elevationData = TileModel::ElevationData( hfLayer, true );
     }
 
-    // Now, if there are any color layers that did not get built, create them with an empty
-    // image so the shaders have something to draw.
-    osg::ref_ptr<osg::Image> emptyImage;
-    osgTerrain::Locator* locator = model->_elevationData.getHFLayer()->getLocator();
-
-    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
-    {
-        ImageLayer* layer = i->get();
-
-        if ( layer->getEnabled() && !layer->isKeyValid(key) )
-        {
-            if ( !emptyImage.valid() )
-                emptyImage = ImageUtils::createEmptyImage();
-
-            model->_colorData[i->get()->getUID()] = TileModel::ColorData(
-                layer,
-                emptyImage.get(),
-                locator,
-                key.getLevelOfDetail(),
-                key,
-                true );
-        }
-    }
-
-    // Ready to create the actual tile.
-    //AssembleTile assemble;
-    //assemble.init( key, mapInfo, _terrainOptions, model.get(), mapf.terrainMaskLayers() );
-    //assemble.execute();
-
     // if we're using LOD blending, find and add the parent's state set.
     if ( out_hasLodBlendedLayers && key.getLevelOfDetail() > 0 && _liveTiles.valid() )
     {
@@ -353,5 +342,4 @@ TileModelFactory::createTileModel(const TileKey&           key,
     }
 
     out_model = model.release();
-    //out_tile = assemble._node;
 }
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNode b/src/osgEarthDrivers/engine_mp/TileNode
similarity index 89%
copy from src/osgEarthDrivers/engine_quadtree/TileNode
copy to src/osgEarthDrivers/engine_mp/TileNode
index 6557259..55517a3 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNode
+++ b/src/osgEarthDrivers/engine_mp/TileNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -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_ENGINE_QUADTREE_TILE_NODE
-#define OSGEARTH_ENGINE_QUADTREE_TILE_NODE 1
+#ifndef OSGEARTH_ENGINE_MP_TILE_NODE
+#define OSGEARTH_ENGINE_MP_TILE_NODE 1
 
 #include "Common"
 #include "TileModel"
@@ -26,14 +26,14 @@
 #include <osg/Group>
 #include <vector>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     using namespace osgEarth;
 
     //------------------------------------------------------------------------
 
     /**
-     * Node that represents a single quadtree terrain tile corresponding to 
+     * Node that represents a single mp terrain tile corresponding to 
      * unique TileKey.
      */
     class TileNode : public osg::Group
@@ -104,6 +104,6 @@ namespace osgEarth_engine_quadtree
         TileNodeGroup() : osg::Group() { }
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_TILE_NODE
+#endif // OSGEARTH_ENGINE_MP_TILE_NODE
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNode.cpp b/src/osgEarthDrivers/engine_mp/TileNode.cpp
similarity index 96%
copy from src/osgEarthDrivers/engine_quadtree/TileNode.cpp
copy to src/osgEarthDrivers/engine_mp/TileNode.cpp
index b72752a..1beb15f 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
+++ b/src/osgEarthDrivers/engine_mp/TileNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -23,7 +23,7 @@
 #include <osg/NodeCallback>
 #include <osg/NodeVisitor>
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 using namespace OpenThreads;
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry b/src/osgEarthDrivers/engine_mp/TileNodeRegistry
similarity index 90%
copy from src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
copy to src/osgEarthDrivers/engine_mp/TileNodeRegistry
index cd64a29..4353d26 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
+++ b/src/osgEarthDrivers/engine_mp/TileNodeRegistry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,15 +16,15 @@
 * 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_TILE_NODE_REGISTRY
-#define OSGEARTH_ENGINE_QUADTREE_TILE_NODE_REGISTRY 1
+#ifndef OSGEARTH_ENGINE_MP_TILE_NODE_REGISTRY
+#define OSGEARTH_ENGINE_MP_TILE_NODE_REGISTRY 1
 
 #include "Common"
 #include "TileNode"
 #include <osgEarth/ThreadingUtils>
 #include <map>
 
-namespace osgEarth_engine_quadtree
+namespace osgEarth_engine_mp
 {
     using namespace osgEarth;
 
@@ -81,6 +81,6 @@ namespace osgEarth_engine_quadtree
         mutable Threading::ReadWriteMutex _tilesMutex;
     };
 
-} // namespace osgEarth_engine_quadtree
+} // namespace osgEarth_engine_mp
 
-#endif // OSGEARTH_ENGINE_QUADTREE_TILE_NODE_REGISTRY
+#endif // OSGEARTH_ENGINE_MP_TILE_NODE_REGISTRY
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp b/src/osgEarthDrivers/engine_mp/TileNodeRegistry.cpp
similarity index 97%
copy from src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
copy to src/osgEarthDrivers/engine_mp/TileNodeRegistry.cpp
index f8ddf42..1759ecf 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
+++ b/src/osgEarthDrivers/engine_mp/TileNodeRegistry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
 */
 #include "TileNodeRegistry"
 
-using namespace osgEarth_engine_quadtree;
+using namespace osgEarth_engine_mp;
 using namespace osgEarth;
 
 #define LC "[TileNodeRegistry] "
diff --git a/src/osgEarthDrivers/engine_osgterrain/Common b/src/osgEarthDrivers/engine_osgterrain/Common
index 9b9e062..3c9d764 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Common
+++ b/src/osgEarthDrivers/engine_osgterrain/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique b/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique
index 5303702..d7c490b 100644
--- a/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique
+++ b/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback b/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback
index 73aa43a..239edae 100644
--- a/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback
+++ b/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback b/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
index b826cb6..3089ad4 100644
--- a/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
+++ b/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory
index 101a5c9..1b63437 100644
--- a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
index 4de5916..e20eaeb 100644
--- a/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp b/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp
index 4bbffb5..4987526 100644
--- a/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp
@@ -18,11 +18,11 @@
  */
 #include "LODFactorCallback"
 
+#include <osgEarth/CullingUtils>
 #include <osg/Math>
 #include <osg/PagedLOD>
 #include <osg/StateSet>
 #include <osg/Uniform>
-#include <osgUtil/CullVisitor>
 
 using namespace osgEarth_engine_osgterrain;
 
@@ -37,7 +37,7 @@ void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
     // 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);
+    osgUtil::CullVisitor* cv = osgEarth::Culling::asCullVisitor(nv);
     osg::LOD::RangeMode rangeMode = lod->getRangeMode();
     float requiredRange = 0.0f;
     float rangeFactor = 1.0f;
diff --git a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique
index 8d4e190..38b7b72 100644
--- a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique
+++ b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp
index 1e1b313..7a2d74b 100644
--- a/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode
index 5e8f33c..f561ce9 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp
index c347610..4f1a886 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -861,7 +861,7 @@ OSGTerrainEngineNode::installShaders()
 
         VirtualProgram* vp = new VirtualProgram();
         vp->setName( "engine_osgterrain:EngineNode" );
-        vp->installDefaultColoringAndLightingShaders(numLayers);
+        //vp->installDefaultColoringAndLightingShaders(numLayers);
 
         getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
     }
@@ -883,7 +883,7 @@ OSGTerrainEngineNode::updateTextureCombining()
 
             VirtualProgram* vp = new VirtualProgram() ;
             vp->setName( "engine_osgterrain:TerrainNode" );
-            vp->installDefaultColoringShaders(numImageLayers);
+            //vp->installDefaultColoringShaders(numImageLayers);
 
             terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
 
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions
index eb30cbd..2154736 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory
index 73ff184..dbd7aa8 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp
index e6c3e95..1489b15 100644
--- a/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory
index d152fae..60a8e78 100644
--- a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp
index 07f599f..ef62387 100644
--- a/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp b/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp
index ef095c1..2785fd7 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory
index 7fc1725..ed87ec5 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp
index 62437a7..80b6029 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique
index 61ee27f..86b64f2 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique
+++ b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp
index 19ffae3..792c73b 100644
--- a/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode
index 662b862..bfa2e7f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp
index 3dfb4aa..55d1d12 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTerrainNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTile b/src/osgEarthDrivers/engine_osgterrain/StreamingTile
index 5eed3e5..8ac870f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTile
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTile
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp b/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp
index 5760575..7cedefd 100644
--- a/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/TerrainNode b/src/osgEarthDrivers/engine_osgterrain/TerrainNode
index 3e80003..85fa569 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TerrainNode
+++ b/src/osgEarthDrivers/engine_osgterrain/TerrainNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp b/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp
index 975f1fc..1f1cd5f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/TerrainNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/Tile b/src/osgEarthDrivers/engine_osgterrain/Tile
index b2651d2..91e18d2 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Tile
+++ b/src/osgEarthDrivers/engine_osgterrain/Tile
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/Tile.cpp b/src/osgEarthDrivers/engine_osgterrain/Tile.cpp
index 208a1dd..aa06c24 100644
--- a/src/osgEarthDrivers/engine_osgterrain/Tile.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/Tile.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/TileBuilder b/src/osgEarthDrivers/engine_osgterrain/TileBuilder
index f7b6c49..698b77f 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TileBuilder
+++ b/src/osgEarthDrivers/engine_osgterrain/TileBuilder
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp b/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp
index ca89060..6aaa662 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp
+++ b/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_osgterrain/TransparentLayer b/src/osgEarthDrivers/engine_osgterrain/TransparentLayer
index be6fe54..025c008 100644
--- a/src/osgEarthDrivers/engine_osgterrain/TransparentLayer
+++ b/src/osgEarthDrivers/engine_osgterrain/TransparentLayer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/Common b/src/osgEarthDrivers/engine_quadtree/Common
index 3b84dd8..067dee6 100644
--- a/src/osgEarthDrivers/engine_quadtree/Common
+++ b/src/osgEarthDrivers/engine_quadtree/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD b/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
index 477f051..4015ddb 100644
--- a/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
+++ b/src/osgEarthDrivers/engine_quadtree/CustomPagedLOD
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback b/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
index 0f8b31f..588ec73 100644
--- a/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
+++ b/src/osgEarthDrivers/engine_quadtree/DynamicLODScaleCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/FileLocationCallback b/src/osgEarthDrivers/engine_quadtree/FileLocationCallback
index ae0533b..19ffea3 100644
--- a/src/osgEarthDrivers/engine_quadtree/FileLocationCallback
+++ b/src/osgEarthDrivers/engine_quadtree/FileLocationCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
index 85900fa..8f53fb9 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
+++ b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
index fa94232..5a552c2 100644
--- a/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/KeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp b/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
index 04ad128..79b595c 100644
--- a/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/LODFactorCallback.cpp
@@ -18,11 +18,11 @@
  */
 #include "LODFactorCallback"
 
+#include <osgEarth/CullingUtils>
 #include <osg/Math>
 #include <osg/PagedLOD>
 #include <osg/StateSet>
 #include <osg/Uniform>
-#include <osgUtil/CullVisitor>
 
 using namespace osgEarth_engine_quadtree;
 
@@ -36,7 +36,7 @@ void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
 {
     // 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);
+    osgUtil::CullVisitor* cv = osgEarth::Culling::asCullVisitor(nv);
     osg::LOD::RangeMode rangeMode = lod->getRangeMode();
     float requiredRange = 0.0f;
     float rangeFactor = 1.0f;
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
index a4143e9..83d5f8f 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineDriver.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
index 4a5b50c..dae3e1e 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp
index fa55151..f3905e4 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -253,7 +253,7 @@ QuadTreeTerrainEngineNode::refresh()
     // 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 );
+    _update_mapf->getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys );
 
     if (_terrainOptions.enableBlending().value())
     {
@@ -302,7 +302,7 @@ QuadTreeTerrainEngineNode::onMapInfoEstablished( const MapInfo& mapInfo )
     // 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 );
+    _update_mapf->getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys );
 
     for( unsigned i=0; i<keys.size(); ++i )
     {
diff --git a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions
index 37b25a4..1f99d79 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions
+++ b/src/osgEarthDrivers/engine_quadtree/QuadTreeTerrainEngineOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -33,7 +33,9 @@ namespace osgEarth { namespace Drivers
             _skirtRatio  ( 0.05 ),
             _quickRelease( true ),
             _lodFallOff  ( 0.0 ),
-            _normalizeEdges( false )
+            _normalizeEdges( false ),
+            _rangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT ),
+            _tilePixelSize( 256 )
         {
             setDriver( "quadtree" );
             fromConfig( _conf );
@@ -55,6 +57,11 @@ namespace osgEarth { namespace Drivers
         optional<bool>& normalizeEdges() { return _normalizeEdges; }
         const optional<bool>& normalizeEdges() const { return _normalizeEdges; }
 
+        optional<osg::LOD::RangeMode>& rangeMode() { return _rangeMode;}
+        const optional<osg::LOD::RangeMode>& rangeMode() const { return _rangeMode;}
+
+        optional<float>& tilePixelSize() { return _tilePixelSize; }
+        const optional<float>& tilePixelSize() const { return _tilePixelSize; }
 
     protected:
         virtual Config getConfig() const {
@@ -63,6 +70,10 @@ namespace osgEarth { namespace Drivers
             conf.updateIfSet( "quick_release_gl_objects", _quickRelease );
             conf.updateIfSet( "lod_fall_off", _lodFallOff );
             conf.updateIfSet( "normalize_edges", _normalizeEdges);
+            conf.updateIfSet( "tile_pixel_size", _tilePixelSize );
+            conf.updateIfSet( "range_mode", "PIXEL_SIZE_ON_SCREEN", _rangeMode, osg::LOD::PIXEL_SIZE_ON_SCREEN );
+            conf.updateIfSet( "range_mode", "DISTANCE_FROM_EYE_POINT", _rangeMode, osg::LOD::DISTANCE_FROM_EYE_POINT);
+
             return conf;
         }
 
@@ -77,12 +88,18 @@ namespace osgEarth { namespace Drivers
             conf.getIfSet( "quick_release_gl_objects", _quickRelease );
             conf.getIfSet( "lod_fall_off", _lodFallOff );
             conf.getIfSet( "normalize_edges", _normalizeEdges );
+            conf.getIfSet( "tile_pixel_size", _tilePixelSize );
+
+            conf.getIfSet( "range_mode", "PIXEL_SIZE_ON_SCREEN", _rangeMode, osg::LOD::PIXEL_SIZE_ON_SCREEN );
+            conf.getIfSet( "range_mode", "DISTANCE_FROM_EYE_POINT", _rangeMode, osg::LOD::DISTANCE_FROM_EYE_POINT);
         }
 
         optional<float> _skirtRatio;
         optional<bool>  _quickRelease;
         optional<float> _lodFallOff;
         optional<bool> _normalizeEdges;
+        optional<osg::LOD::RangeMode> _rangeMode;
+        optional<float> _tilePixelSize;
     };
 
 } } // namespace osgEarth::Drivers
diff --git a/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects b/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
index 9a32417..05de1aa 100644
--- a/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
+++ b/src/osgEarthDrivers/engine_quadtree/QuickReleaseGLObjects
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
index 954c602..605b759 100644
--- a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
+++ b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
index 082705b..a88a6c6 100644
--- a/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/SerialKeyNodeFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -78,24 +78,17 @@ SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileH
     // 3. We are still below the maximim LOD.
     bool wrapInPagedLOD =
         (tileHasRealData || (_options.minLOD().isSet() && model->_tileKey.getLOD() < *_options.minLOD())) &&
-        //(tileHasRealData || _options.minLOD().isSet()) &&
         !osgEarth::Registry::instance()->isBlacklisted( uri ) &&
         model->_tileKey.getLOD() < *_options.maxLOD();
 
     if ( wrapInPagedLOD )
     {
         osg::BoundingSphere bs = tileNode->getBound();
+      
         float maxRange = FLT_MAX;
         
-#if 0
-        //Compute the min range based on the actual bounds of the tile.  This can break down if you have very high resolution
-        //data with elevation variations and you can run out of memory b/c the elevation change is greater than the actual size of the tile so you end up
-        //inifinitely subdividing (or at least until you run out of data or memory)
-        double minRange = bs.radius() * _options.minTileRangeFactor().value();
-#else        
-        //double origMinRange = bs.radius() * _options.minTileRangeFactor().value();        
         //Compute the min range based on the 2D size of the tile
-        GeoExtent extent = model->_tileKey.getExtent();        
+        GeoExtent extent = model->_tileKey.getExtent();
         GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE);
         GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE);
         osg::Vec3d ll, ur;
@@ -103,15 +96,31 @@ SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileH
         upperRight.toWorld( ur );
         double radius = (ur - ll).length() / 2.0;
         float minRange = (float)(radius * _options.minTileRangeFactor().value());
-#endif
 
+        
         // create a PLOD so we can keep subdividing:
         osg::PagedLOD* plod = new CustomPagedLOD( _liveTiles.get(), _deadTiles.get() );
         plod->setCenter( bs.center() );
-        plod->addChild( tileNode, minRange, maxRange );
-
+        plod->addChild( tileNode );
+        plod->setRangeMode( *_options.rangeMode() );
         plod->setFileName( 1, uri );
-        plod->setRange   ( 1, 0, minRange );
+  
+
+        if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN)
+        {
+            static const float sqrt2 = sqrt(2.0f);
+
+            minRange = 0;
+            maxRange = (*_options.tilePixelSize()) * sqrt2;
+            plod->setRange( 0, minRange, maxRange  );
+            plod->setRange( 1, maxRange, FLT_MAX );            
+        }
+        else
+        {
+            plod->setRange( 0, minRange, maxRange );                
+            plod->setRange( 1, 0, minRange );        
+        }        
+                        
 
         plod->setUserData( new MapNode::TileRangeData(minRange, maxRange) );
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TerrainNode b/src/osgEarthDrivers/engine_quadtree/TerrainNode
index b9243ce..12d6ece 100644
--- a/src/osgEarthDrivers/engine_quadtree/TerrainNode
+++ b/src/osgEarthDrivers/engine_quadtree/TerrainNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp b/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
index 143c200..f1d9338 100644
--- a/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TerrainNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModel b/src/osgEarthDrivers/engine_quadtree/TileModel
index cc093bc..ec44306 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModel
+++ b/src/osgEarthDrivers/engine_quadtree/TileModel
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
index ba5b1ef..ebff291 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
index 86221d4..828d513 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelCompiler.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -19,6 +19,8 @@
 #include "TileModelCompiler"
 
 #include <osgEarth/Locators>
+#include <osgEarth/Registry>
+#include <osgEarth/Capabilities>
 #include <osgEarth/TextureCompositor>
 #include <osgEarthSymbology/Geometry>
 #include <osgEarthSymbology/MeshConsolidator>
@@ -121,8 +123,11 @@ namespace
             unifiedSkirtTexCoords       = 0L;
             unifiedStitchSkirtTexCoords = 0L;
             unifiedSurfaceTexCoords     = 0L;
+            useVBOs = !Registry::capabilities().preferDisplayListsForStaticGeometry();
         }
 
+        bool                     useVBOs;
+
         const TileModel*         model;                         // the tile's data model
         const MaskLayerVector&   maskLayers;                    // map-global masking layer set
         osg::ref_ptr<GeoLocator> geoLocator;                    // tile locator adjusted to geocentric
@@ -156,7 +161,9 @@ namespace
         double                   i_sampleFactor;
         double                   j_sampleFactor;
         double                   scaleHeight;
-
+        unsigned                 originalNumRows;
+        unsigned                 originalNumCols;
+        
         // for masking/stitching:
         MaskRecordVector         maskRecords;
         osg::Geometry*           stitching_skirts;
@@ -227,7 +234,7 @@ namespace
               if (x_match && y_match)
               {
                 osg::Geometry* mask_geom = new osg::Geometry();
-                mask_geom->setUseVertexBufferObjects(true);
+                mask_geom->setUseVertexBufferObjects(d.useVBOs);
                 d.surfaceGeode->addDrawable(mask_geom);
                 d.maskRecords.push_back( MaskRecord(boundary, min_ndc, max_ndc, mask_geom) );
               }
@@ -237,7 +244,7 @@ namespace
         if (d.maskRecords.size() > 0)
         {
           d.stitching_skirts = new osg::Geometry();
-          d.stitching_skirts->setUseVertexBufferObjects(true);
+          d.stitching_skirts->setUseVertexBufferObjects(d.useVBOs);
           d.surfaceGeode->addDrawable( d.stitching_skirts );
 
           d.ss_verts = new osg::Vec3Array();
@@ -257,13 +264,17 @@ namespace
     {
         d.numRows = 8;
         d.numCols = 8;
+        d.originalNumRows = 8;
+        d.originalNumCols = 8;        
 
         // read the row/column count and skirt size from the model:
         osgTerrain::HeightFieldLayer* hflayer = d.model->_elevationData.getHFLayer();
         if (hflayer)
         {
             d.numCols = hflayer->getNumColumns();
-            d.numRows = hflayer->getNumRows();
+            d.numRows = hflayer->getNumRows();         
+            d.originalNumCols = d.numCols;
+            d.originalNumRows = d.numRows;
         }
 
         // calculate the elevation sampling factors that we'll use to step though
@@ -272,15 +283,12 @@ namespace
         d.j_sampleFactor = 1.0f;
 
         if ( sampleRatio != 1.0f )
-        {
-            unsigned originalNumCols = d.numCols;
-            unsigned originalNumRows = d.numRows;
-
-            d.numCols = osg::maximum((unsigned int) (float(originalNumCols)*sqrtf(sampleRatio)), 4u);
-            d.numRows = osg::maximum((unsigned int) (float(originalNumRows)*sqrtf(sampleRatio)), 4u);
+        {            
+            d.numCols = osg::maximum((unsigned int) (float(d.originalNumCols)*sqrtf(sampleRatio)), 4u);
+            d.numRows = osg::maximum((unsigned int) (float(d.originalNumRows)*sqrtf(sampleRatio)), 4u);
 
-            d.i_sampleFactor = double(originalNumCols-1)/double(d.numCols-1);
-            d.j_sampleFactor = double(originalNumRows-1)/double(d.numRows-1);
+            d.i_sampleFactor = double(d.originalNumCols-1)/double(d.numCols-1);
+            d.j_sampleFactor = double(d.originalNumRows-1)/double(d.numRows-1);
         }
 
 
@@ -1330,28 +1338,27 @@ namespace
                     }
                 }
             }
-        }
-
+        }        
         
         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 );            
-
+            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)
+            if (w_neighbor.valid() && w_neighbor->getNumColumns() == d.originalNumCols && w_neighbor->getNumRows() == d.originalNumRows)            
             {                                     
                 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++)
                 {
@@ -1359,8 +1366,12 @@ namespace
                     {                          
                         osg::Vec3d ndc( (double)(i - static_cast<int>(d.numCols-1))/(double)(d.numCols-1), ((double)j)/(double)(d.numRows-1), 0.0);                                                                        
 
+                        // use the sampling factor to determine the lookup index:
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
+
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = w_neighbor->getHeight( i, j );
+                        float heightValue = w_neighbor->getHeight( i_equiv, j_equiv );
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1429,7 +1440,7 @@ namespace
 
                         
             //Recalculate the east side
-            if (e_neighbor.valid() && e_neighbor->getNumColumns() == d.numCols && e_neighbor->getNumRows() == d.numRows)
+            if (e_neighbor.valid() && e_neighbor->getNumColumns() == d.originalNumCols && e_neighbor->getNumRows() == d.originalNumRows)            
             {                           
                 osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
                 boundaryVerts->reserve( 2 * d.numRows );
@@ -1443,9 +1454,12 @@ namespace
                     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);
+
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
                         
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = e_neighbor->getHeight( i, j );
+                        float heightValue = e_neighbor->getHeight( i_equiv, j_equiv );
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1511,7 +1525,7 @@ namespace
             }
 
             //Recalculate the north side
-            if (n_neighbor.valid() && n_neighbor->getNumColumns() == d.numCols && n_neighbor->getNumRows() == d.numRows)
+            if (n_neighbor.valid() && n_neighbor->getNumColumns() == d.originalNumCols && n_neighbor->getNumRows() == d.originalNumRows)            
             {                 
                 osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
                 boundaryVerts->reserve( 2 * d.numCols );
@@ -1526,9 +1540,12 @@ namespace
                     {                           
                         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);                        
+
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
                         
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = n_neighbor->getHeight( i, j );
+                        float heightValue = n_neighbor->getHeight( i_equiv, j_equiv );
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1594,7 +1611,7 @@ namespace
             }
 
             //Recalculate the south side
-            if (s_neighbor.valid() && s_neighbor->getNumColumns() == d.numCols && s_neighbor->getNumRows() == d.numRows)
+            if (s_neighbor.valid() && s_neighbor->getNumColumns() == d.originalNumCols && s_neighbor->getNumRows() == d.originalNumRows)            
             {                
                 osg::ref_ptr< osg::Vec3Array > boundaryVerts = new osg::Vec3Array();
                 boundaryVerts->reserve( 2 * d.numCols );
@@ -1608,9 +1625,12 @@ namespace
                     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);                                                
+
+                        unsigned i_equiv = d.i_sampleFactor==1.0 ? i : (unsigned) (double(i)*d.i_sampleFactor);
+                        unsigned j_equiv = d.j_sampleFactor==1.0 ? j : (unsigned) (double(j)*d.j_sampleFactor);
                         
                         //TODO:  Should probably use an interpolated method here
-                        float heightValue = s_neighbor->getHeight( i, j );
+                        float heightValue = s_neighbor->getHeight( i_equiv, j_equiv );                        
                         ndc.z() = heightValue;                        
 
                         osg::Vec3d model;
@@ -1760,8 +1780,6 @@ _texCompositor         ( texCompositor ),
 _optimizeTriOrientation( optimizeTriOrientation ),
 _options               ( options )
 {
-    //nop
-
     _cullByTraversalMask = new CullByTraversalMask(*options.secondaryTraversalMask());
 }
 
@@ -1782,7 +1800,7 @@ TileModelCompiler::compile(const TileModel* model,
 
     // A Geode/Geometry for the surface:
     d.surface = new osg::Geometry();
-    d.surface->setUseVertexBufferObjects(true);
+    d.surface->setUseVertexBufferObjects(d.useVBOs);
     d.surfaceGeode = new osg::Geode();
     d.surfaceGeode->addDrawable( d.surface );
     d.surfaceGeode->setNodeMask( *_options.primaryTraversalMask() );
@@ -1797,12 +1815,7 @@ TileModelCompiler::compile(const TileModel* model,
     if ( d.createSkirt )
     {
         d.skirt = new osg::Geometry();
-        d.skirt->setUseVertexBufferObjects(true);
-
-        //d.skirtGeode = new osg::Geode();
-        //d.skirtGeode->addDrawable( d.skirt );
-        //d.skirtGeode->setNodeMask( *_options.secondaryTraversalMask() );
-        //xform->addChild( d.skirtGeode );
+        d.skirt->setUseVertexBufferObjects(d.useVBOs);
 
         // slightly faster than a separate geode:
         d.skirt->setDataVariance( osg::Object::DYNAMIC ); // since we're using a custom cull callback
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelFactory b/src/osgEarthDrivers/engine_quadtree/TileModelFactory
index 6dbe44c..0b80843 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelFactory
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -64,12 +64,12 @@ namespace osgEarth_engine_quadtree
         }
 
         bool getOrCreateHeightField( 
-                const MapFrame&                       frame,  
+                const MapFrame&                 frame,
                 const TileKey&                  key,
                 bool                            fallback,
-                osg::ref_ptr<osg::HeightField>& out_hf,            
-                bool*                           out_isFallback =0L,   
-                bool                            convertToHAE   =true,         
+                osg::ref_ptr<osg::HeightField>& out_hf,
+                bool*                           out_isFallback =0L,
+                bool                            convertToHAE   =true,
                 ElevationSamplePolicy           samplePolicy   =SAMPLE_FIRST_VALID,
                 ProgressCallback*               progress       =0L ) const
         {                
@@ -79,16 +79,16 @@ namespace osgEarth_engine_quadtree
             cachekey._fallback     = fallback;
             cachekey._convertToHAE = convertToHAE;
             cachekey._samplePolicy = samplePolicy;
-            LRUCache<HFKey,HFValue>::Record rec = _cache.get( cachekey );        
 
             bool hit = false;
-            if ( rec.valid() )
+            LRUCache<HFKey,HFValue>::Record rec;
+            if ( _cache.get(cachekey, rec) )
             {
                 out_hf = rec.value()._hf.get();
                 if ( out_isFallback )
-                    *out_isFallback = rec.value()._isFallback;                        
-                return true;            
-            }                
+                    *out_isFallback = rec.value()._isFallback;
+                return true;
+            }
 
             bool isFallback;
 
diff --git a/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp b/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
index ae2bdcf..0e6fcb4 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileModelFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNode b/src/osgEarthDrivers/engine_quadtree/TileNode
index 6557259..b0fbb36 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNode
+++ b/src/osgEarthDrivers/engine_quadtree/TileNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNode.cpp b/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
index b72752a..5816264 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
index cd64a29..d92574c 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
+++ b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
index f8ddf42..34987c2 100644
--- a/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
+++ b/src/osgEarthDrivers/engine_quadtree/TileNodeRegistry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR b/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR
index e2cd37c..1fde287 100644
--- a/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR
+++ b/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp b/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp
index 3aee936..3a5e13c 100644
--- a/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp
+++ b/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -52,18 +52,16 @@ _filters          ( filters )
 
         std::string expr;
         std::string from = OGR_FD_GetName( OGR_L_GetLayerDefn( _layerHandle ));        
-        //If the from field contains a space, quote it.
-        if (from.find(" ") != std::string::npos)
+        
+        //Quote the layer name.
+        std::string driverName = OGR_Dr_GetName( OGR_DS_GetDriver( dsHandle ) );
+        std::string delim = "'";  //Use single quotes by default
+        if (driverName.compare("PostgreSQL") == 0)
         {
-            std::string driverName = OGR_Dr_GetName( OGR_DS_GetDriver( dsHandle ) );
-            std::string delim = "'";  //Use single quotes by default
-            if (driverName.compare("PostgreSQL") == 0)
-            {
-                //PostgreSQL uses double quotes as identifier delimeters
-                delim = "\"";
-            }            
-            from = delim + from + delim;            
-        }
+            //PostgreSQL uses double quotes as identifier delimeters
+            delim = "\"";
+        }            
+        from = delim + from + delim;                    
 
         if ( query.expression().isSet() )
         {
diff --git a/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp b/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp
index be2f509..82eeb34 100644
--- a/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp
+++ b/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -154,9 +154,9 @@ public:
             // attempt to open the dataset:
             int openMode = _options.openWrite().isSet() && _options.openWrite().value() ? 1 : 0;
 
-	        _dsHandle = OGROpen( _source.c_str(), openMode, &_ogrDriverHandle );
-	        if ( _dsHandle )
-	        {
+            _dsHandle = OGROpen( _source.c_str(), openMode, &_ogrDriverHandle );
+            if ( _dsHandle )
+            {
                 if (openMode == 1) _writable = true;
                 
                 if ( _options.layer().isSet() )
@@ -164,7 +164,7 @@ public:
                     _layerIndex = _options.layer().value();
                 }                
 
-		        _layerHandle = OGR_DS_GetLayer( _dsHandle, _layerIndex );
+                _layerHandle = OGR_DS_GetLayer( _dsHandle, _layerIndex );
                 if ( _layerHandle )
                 {                                     
                     GeoExtent extent;
@@ -204,8 +204,8 @@ public:
                         std::stringstream buf;
                         const char* name = OGR_FD_GetName( OGR_L_GetLayerDefn( _layerHandle ) );
                         buf << "CREATE SPATIAL INDEX ON " << name; 
-					    std::string bufStr;
-					    bufStr = buf.str();
+                        std::string bufStr;
+                        bufStr = buf.str();
                         OE_DEBUG << LC << "SQL: " << bufStr << std::endl;
                         OGR_DS_ExecuteSQL( _dsHandle, bufStr.c_str(), 0L, 0L );
                     }
@@ -252,7 +252,7 @@ public:
                         _geometryType = Geometry::TYPE_MULTI;
                     }
                 }
-	        }
+            }
             else
             {
                 OE_INFO << LC << "failed to open dataset \"" << _source << "\"" << std::endl;
@@ -286,9 +286,9 @@ public:
             // Each cursor requires its own DS handle so that multi-threaded access will work.
             // The cursor impl will dispose of the new DS handle.
 
-	        OGRDataSourceH dsHandle = OGROpenShared( _source.c_str(), 0, &_ogrDriverHandle );
-	        if ( dsHandle )
-	        {
+            OGRDataSourceH dsHandle = OGROpenShared( _source.c_str(), 0, &_ogrDriverHandle );
+            if ( dsHandle )
+            {
                 OGRLayerH layerHandle = OGR_DS_GetLayer( dsHandle, _layerIndex );
 
                 return new FeatureCursorOGR( 
diff --git a/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions b/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions
index 5d57a29..8817962 100644
--- a/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions
+++ b/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/feature_tfs/FeatureSourceTFS.cpp b/src/osgEarthDrivers/feature_tfs/FeatureSourceTFS.cpp
index 53d5162..82e0049 100644
--- a/src/osgEarthDrivers/feature_tfs/FeatureSourceTFS.cpp
+++ b/src/osgEarthDrivers/feature_tfs/FeatureSourceTFS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -47,6 +47,8 @@ using namespace osgEarth::Util;
 using namespace osgEarth::Features;
 using namespace osgEarth::Drivers;
 
+#define OGR_SCOPED_LOCK GDAL_SCOPED_LOCK
+
 /**
  * A FeatureSource that reads features from a TFS layer
  * 
@@ -58,9 +60,7 @@ public:
       FeatureSource( options ),
       _options     ( options ),
       _layerValid(false)
-    {        
-        _geojsonDriver = OGRGetDriverByName( "GeoJSON" );
-        _gmlDriver     = OGRGetDriverByName( "GML" );
+    {                
     }
 
     /** Destruct the object, cleaning up and OGR handles. */
@@ -125,9 +125,12 @@ public:
     bool getFeatures( const std::string& buffer, const std::string& mimeType, FeatureList& features )
     {        
         // find the right driver for the given mime type
+        OGR_SCOPED_LOCK;
+                
+        // find the right driver for the given mime type
         OGRSFDriverH ogrDriver =
-            isJSON(mimeType) ? _geojsonDriver :
-            isGML(mimeType)  ? _gmlDriver :
+            isJSON(mimeType) ? OGRGetDriverByName( "GeoJSON" ) :
+            isGML(mimeType)  ? OGRGetDriverByName( "GML" ) :
             0L;
 
         // fail if we can't find an appropriate OGR driver:
@@ -334,8 +337,7 @@ private:
     const TFSFeatureOptions         _options;    
     FeatureSchema                   _schema;
     osg::ref_ptr<CacheBin>          _cacheBin;
-    osg::ref_ptr<osgDB::Options>    _dbOptions;
-    OGRSFDriverH                    _geojsonDriver, _gmlDriver;
+    osg::ref_ptr<osgDB::Options>    _dbOptions;    
     TFSLayer                        _layer;
     bool                            _layerValid;
 };
diff --git a/src/osgEarthDrivers/feature_tfs/TFSFeatureOptions b/src/osgEarthDrivers/feature_tfs/TFSFeatureOptions
index bc60e23..673490b 100644
--- a/src/osgEarthDrivers/feature_tfs/TFSFeatureOptions
+++ b/src/osgEarthDrivers/feature_tfs/TFSFeatureOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp b/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp
index 59fb01a..a9ce0b6 100644
--- a/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp
+++ b/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -61,8 +61,6 @@ public:
       FeatureSource( options ),
       _options     ( options )
     {        
-        _geojsonDriver = OGRGetDriverByName( "GeoJSON" );
-        _gmlDriver     = OGRGetDriverByName( "GML" );
     }
 
     /** Destruct the object, cleaning up and OGR handles. */
@@ -110,7 +108,7 @@ public:
                 _options.url()->full() +
                 sep + 
                 "SERVICE=WFS&VERSION=1.0.0&REQUEST=GetCapabilities";
-        }
+        }        
 
         _capabilities = WFSCapabilitiesReader::read( capUrl, _dbOptions.get() );
         if ( !_capabilities.valid() )
@@ -124,6 +122,14 @@ public:
         }
     }
 
+    void saveResponse(const std::string buffer, const std::string& filename)
+    {
+        std::ofstream fout;
+        fout.open(filename.c_str(), std::ios::out | std::ios::binary);        
+        fout.write(buffer.c_str(), buffer.size());        
+        fout.close();
+    }
+
 
     /** Called once at startup to create the profile for this feature set. Successful profile
         creation implies that the datasource opened succesfully. */
@@ -184,11 +190,16 @@ public:
 
     bool getFeatures( const std::string& buffer, const std::string& mimeType, FeatureList& features )
     {
+        OGR_SCOPED_LOCK;        
+
+        bool json = isJSON( mimeType );
+        bool gml  = isGML( mimeType );
+
         // find the right driver for the given mime type
         OGRSFDriverH ogrDriver =
-            isJSON(mimeType) ? _geojsonDriver :
-            isGML(mimeType)  ? _gmlDriver :
-            0L;
+            json ? OGRGetDriverByName( "GeoJSON" ) :
+            gml  ? OGRGetDriverByName( "GML" ) :
+            0L;        
 
         // fail if we can't find an appropriate OGR driver:
         if ( !ogrDriver )
@@ -198,7 +209,25 @@ public:
             return false;
         }
 
-        OGRDataSourceH ds = OGROpen( buffer.c_str(), FALSE, &ogrDriver );
+        std::string tmpName;
+
+        OGRDataSourceH ds = 0;
+        //GML needs to be saved to a temp file to load from disk.  GeoJSON can be loaded directly from memory
+        if (gml)
+        {
+            std::string ext = getExtensionForMimeType( mimeType );
+            //Save the response to a temp file            
+            std::string tmpPath = getTempPath();        
+            tmpName = getTempName(tmpPath, ext);
+            saveResponse(buffer, tmpName );
+            ds = OGROpen( tmpName.c_str(), FALSE, &ogrDriver );
+        }
+        else if (json)
+        {
+            //Open GeoJSON directly from memory
+            ds = OGROpen( buffer.c_str(), FALSE, &ogrDriver );
+        }        
+
         
         if ( !ds )
         {
@@ -231,6 +260,12 @@ public:
 
         // Destroy the datasource
         OGR_DS_Destroy( ds );
+
+        //Delete the temp file if one was created
+        if (!tmpName.empty())
+        {
+            remove( tmpName.c_str() );
+        }
         
         return true;
     }
@@ -262,9 +297,9 @@ public:
     }
 
     bool isGML( const std::string& mime ) const
-    {
+    {        
         return
-            startsWith(mime, "text/xml");
+            startsWith(mime, "text/xml");            
     }
 
 
@@ -283,7 +318,7 @@ public:
     std::string createURL(const Symbology::Query& query)
     {
         std::stringstream buf;
-        buf << _options.url()->full() << "?SERVICE=WFS&VERSION=1.0.0&REQUEST=getfeature";
+        buf << _options.url()->full() << "?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature";
         buf << "&TYPENAME=" << _options.typeName().get();
         
         std::string outputFormat = "geojson";
@@ -315,7 +350,7 @@ public:
     {
         FeatureCursor* result = 0L;
 
-        std::string url = createURL( query );
+        std::string url = createURL( query );        
 
         // check the blacklist:
         if ( Registry::instance()->isBlacklisted(url) )
@@ -405,8 +440,7 @@ private:
     osg::ref_ptr< FeatureProfile >  _featureProfile;
     FeatureSchema                   _schema;
     osg::ref_ptr<CacheBin>          _cacheBin;
-    osg::ref_ptr<osgDB::Options>    _dbOptions;
-    OGRSFDriverH                    _geojsonDriver, _gmlDriver;
+    osg::ref_ptr<osgDB::Options>    _dbOptions;    
 };
 
 
diff --git a/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions b/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions
index 21dbf9b..2e9de02 100644
--- a/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions
+++ b/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/gdal/GDALOptions b/src/osgEarthDrivers/gdal/GDALOptions
index 4a80974..abfc543 100644
--- a/src/osgEarthDrivers/gdal/GDALOptions
+++ b/src/osgEarthDrivers/gdal/GDALOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -60,9 +60,15 @@ namespace osgEarth { namespace Drivers
         optional<URI>& url() { return _url; }
         const optional<URI>& url() const { return _url; }
 
+        optional<std::string>& connection() { return _connection; }
+        const optional<std::string>& connection() const { return _connection; }
+
         optional<std::string>& extensions() { return _extensions; }
         const optional<std::string>& extensions() const { return _extensions; }
         
+        optional<std::string>& blackExtensions() { return _blackExtensions; }
+        const optional<std::string>& blackExtensions() const { return _blackExtensions; }
+        
         optional<ElevationInterpolation>& interpolation() { return _interpolation; }
         const optional<ElevationInterpolation>& interpolation() const { return _interpolation; }
 
@@ -111,7 +117,9 @@ namespace osgEarth { namespace Drivers
         {
             Config conf = TileSourceOptions::getConfig();
             conf.updateIfSet( "url", _url );
+            conf.updateIfSet( "connection", _connection );
             conf.updateIfSet( "extensions", _extensions );
+            conf.updateIfSet( "black_extensions", _blackExtensions );
 
             if ( _interpolation.isSet() ) {
                 if ( _interpolation.value() == osgEarth::INTERP_NEAREST ) conf.update( "interpolation", "nearest" );
@@ -138,7 +146,9 @@ namespace osgEarth { namespace Drivers
 
         void fromConfig( const Config& conf ) {
             conf.getIfSet( "url", _url );
+            conf.getIfSet( "connection", _connection );
             conf.getIfSet( "extensions", _extensions );
+            conf.getIfSet( "black_extensions", _blackExtensions );
             std::string in = conf.value( "interpolation" );
             if ( in == "nearest" ) _interpolation = osgEarth::INTERP_NEAREST;
             else if ( in == "average" ) _interpolation = osgEarth::INTERP_AVERAGE;
@@ -154,7 +164,9 @@ namespace osgEarth { namespace Drivers
         }
 
         optional<URI>                    _url;
+        optional<std::string>            _connection;
         optional<std::string>            _extensions;
+        optional<std::string>			 _blackExtensions;
         optional<ElevationInterpolation> _interpolation;
         optional<bool>                   _interpolateImagery;
         optional<unsigned int>           _maxDataLevel;
diff --git a/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp b/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp
index a2955d6..f70c060 100644
--- a/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp
+++ b/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -115,7 +115,7 @@ typedef struct
 } BandProperty;
 
 static void
-getFiles(const std::string &file, const std::vector<std::string> &exts, std::vector<std::string> &files)
+getFiles(const std::string &file, const std::vector<std::string> &exts, const std::vector<std::string> &blackExts, std::vector<std::string> &files)
 {
     if (osgDB::fileType(file) == osgDB::DIRECTORY)
     {
@@ -124,11 +124,12 @@ getFiles(const std::string &file, const std::vector<std::string> &exts, std::vec
         {
             if (*itr == "." || *itr == "..") continue;
             std::string f = osgDB::concatPaths(file, *itr);
-            getFiles(f, exts, files);
+            getFiles(f, exts, blackExts, files);
         }
     }
     else
     {
+		std::string ext = osgDB::getFileExtension(file);
         bool fileValid = false;
         //If we have no _extensions specified, assume we should try everything
         if (exts.size() == 0)
@@ -138,7 +139,6 @@ getFiles(const std::string &file, const std::vector<std::string> &exts, std::vec
         else
         {
             //Only accept files with the given _extensions
-            std::string ext = osgDB::getFileExtension(file);
             for (unsigned int i = 0; i < exts.size(); ++i)
             {
                 if (osgDB::equalCaseInsensitive(ext, exts[i]))
@@ -147,7 +147,17 @@ getFiles(const std::string &file, const std::vector<std::string> &exts, std::vec
                     break;
                 }
             }
-        }
+		}
+
+		//Ignore any files that have blacklisted extensions
+        for (unsigned int i = 0; i < blackExts.size(); ++i)
+        {
+			if (osgDB::equalCaseInsensitive(ext, blackExts[i]))
+			{
+				fileValid = false;
+				break;
+			}
+		}
         
         if (fileValid)
         {
@@ -696,38 +706,63 @@ public:
         }
 
         if (useExternalDataset == false &&
-            (!_options.url().isSet() || _options.url()->empty()) )
+            (!_options.url().isSet() || _options.url()->empty()) &&
+            (!_options.connection().isSet() || _options.connection()->empty()) )
         {
-            return Status::Error( "No URL or directory specified" );
+            return Status::Error( "No URL, directory, or connection string specified" );
         }
 
-        URI uri = _options.url().value();
+        // source connection:
+        std::string source;
+        
+        if ( _options.url().isSet() )
+            source = _options.url()->full();
+        else if ( _options.connection().isSet() )
+            source = _options.connection().value();
 
+        //URI uri = _options.url().value();
+        
         if (useExternalDataset == false)
         {
-            StringTokenizer izer( ";" );
-            StringVector exts;
-            izer.tokenize( *_options.extensions(), exts );
-
-            //std::vector<std::string> exts;
+            std::vector<std::string> files;
 
-            //tokenize( _options.extensions().value(), exts, ";");
-            for (unsigned int i = 0; i < exts.size(); ++i)
+            if ( _options.url().isSet() )
             {
-                OE_DEBUG << LC << "Using Extension: " << exts[i] << std::endl;
-            }
-            std::vector<std::string> files;
-            getFiles(uri.full(), exts, files);
+                // collect a list of files, filtering by extension if necessary
+                StringTokenizer izer( ";" );
+                StringVector exts;
+                izer.tokenize( *_options.extensions(), exts );
 
-            OE_INFO << LC << "Driver found " << files.size() << " files:" << std::endl;
-            for (unsigned int i = 0; i < files.size(); ++i)
+				StringVector blackExts;
+				izer.tokenize( *_options.blackExtensions(), blackExts );
+
+                for (unsigned int i = 0; i < exts.size(); ++i)
+                {
+                    OE_DEBUG << LC << "Using Extension: " << exts[i] << std::endl;
+                }
+
+				for (unsigned int i = 0; i < blackExts.size(); ++i)
+				{
+					OE_DEBUG << LC << "Blacklisting Extension: " << blackExts[i] << std::endl;
+				}
+
+                getFiles(source, exts, blackExts, files);
+
+                OE_INFO << LC << "Driver found " << files.size() << " files:" << std::endl;
+                for (unsigned int i = 0; i < files.size(); ++i)
+                {
+                    OE_INFO << LC << "" << files[i] << std::endl;
+                }
+            }
+            else
             {
-                OE_INFO << LC << "" << files[i] << std::endl;
+                // just add the connection string as the single source.
+                files.push_back( source );
             }
 
             if (files.empty())
             {
-                return Status::Error( "Could not find any valid files" );
+                return Status::Error( "Could not find any valid input." );
             }
 
             //If we found more than one file, try to combine them into a single logical dataset
@@ -876,7 +911,7 @@ public:
         if ( !src_srs.valid() )
         {
             // not found in the dataset; try loading a .prj file
-            std::string prjLocation = osgDB::getNameLessExtension( uri.full() ) + std::string(".prj");
+            std::string prjLocation = osgDB::getNameLessExtension(source) + std::string(".prj");
 
             ReadResult r = URI(prjLocation).readString( _dbOptions.get() );
             if ( r.succeeded() )
@@ -887,7 +922,7 @@ public:
             if ( !src_srs.valid() )
             {
                 return Status::Error( Stringify()
-                    << "Dataset has no spatial reference information (" << uri.full() << ")" );
+                    << "Dataset has no spatial reference information (" << source << ")" );
             }
         }
 
@@ -896,8 +931,8 @@ public:
         
         bool hasGCP = _srcDS->GetGCPCount() > 0 && _srcDS->GetGCPProjection();
         bool isRotated = _geotransform[2] != 0.0 || _geotransform[4];
-        if (hasGCP) OE_DEBUG << LC << uri.full() << " has GCP georeferencing" << std::endl;
-        if (isRotated) OE_DEBUG << LC << uri.full() << " is rotated " << std::endl;
+        if (hasGCP) OE_DEBUG << LC << source << " has GCP georeferencing" << std::endl;
+        if (isRotated) OE_DEBUG << LC << source << " is rotated " << std::endl;
         bool requiresReprojection = hasGCP || isRotated;
 
         const Profile* profile = NULL;
@@ -1025,7 +1060,7 @@ public:
                 warpedSRSWKT,
                 minX, minY, maxX, maxY);
 
-            OE_INFO << LC << "" << uri.full() << " is projected, SRS = " 
+            OE_INFO << LC << "" << source << " is projected, SRS = " 
                 << warpedSRSWKT << std::endl;
                 //<< _warpedDS->GetProjectionRef() << std::endl;
         }
@@ -1041,7 +1076,7 @@ public:
         if (_options.maxDataLevel().isSet())
         {
             _maxDataLevel = _options.maxDataLevel().value();
-            OE_INFO << "Using override max data level " << _maxDataLevel << std::endl;
+            OE_INFO << _options.url().value().full() << " using override max data level " << _maxDataLevel << std::endl;
         }
         else
         {
@@ -1060,7 +1095,7 @@ public:
                 }
             }
 
-            OE_NOTICE << LC << "Max Data Level: " << _maxDataLevel << std::endl;
+            OE_NOTICE << LC << _options.url().value().full() << " max Data Level: " << _maxDataLevel << std::endl;
         }
 
         osg::ref_ptr< SpatialReference > srs = SpatialReference::create( warpedSRSWKT );
diff --git a/src/osgEarthDrivers/kml/KML b/src/osgEarthDrivers/kml/KML
index 00f9089..01e81b6 100644
--- a/src/osgEarthDrivers/kml/KML
+++ b/src/osgEarthDrivers/kml/KML
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KMLOptions b/src/osgEarthDrivers/kml/KMLOptions
index a9df4b0..5b0ead8 100644
--- a/src/osgEarthDrivers/kml/KMLOptions
+++ b/src/osgEarthDrivers/kml/KMLOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KMLReader b/src/osgEarthDrivers/kml/KMLReader
index 2c441da..8d2a130 100644
--- a/src/osgEarthDrivers/kml/KMLReader
+++ b/src/osgEarthDrivers/kml/KMLReader
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KMLReader.cpp b/src/osgEarthDrivers/kml/KMLReader.cpp
index c857127..afef868 100644
--- a/src/osgEarthDrivers/kml/KMLReader.cpp
+++ b/src/osgEarthDrivers/kml/KMLReader.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Common b/src/osgEarthDrivers/kml/KML_Common
index 32b3be0..e3ba055 100644
--- a/src/osgEarthDrivers/kml/KML_Common
+++ b/src/osgEarthDrivers/kml/KML_Common
@@ -1,7 +1,7 @@
 
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Container b/src/osgEarthDrivers/kml/KML_Container
index 55be876..4f96c12 100644
--- a/src/osgEarthDrivers/kml/KML_Container
+++ b/src/osgEarthDrivers/kml/KML_Container
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Document b/src/osgEarthDrivers/kml/KML_Document
index e269453..c6c8ad3 100644
--- a/src/osgEarthDrivers/kml/KML_Document
+++ b/src/osgEarthDrivers/kml/KML_Document
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Document.cpp b/src/osgEarthDrivers/kml/KML_Document.cpp
index 4386dec..e421a2c 100644
--- a/src/osgEarthDrivers/kml/KML_Document.cpp
+++ b/src/osgEarthDrivers/kml/KML_Document.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Feature b/src/osgEarthDrivers/kml/KML_Feature
index 964206b..c2236a2 100644
--- a/src/osgEarthDrivers/kml/KML_Feature
+++ b/src/osgEarthDrivers/kml/KML_Feature
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Feature.cpp b/src/osgEarthDrivers/kml/KML_Feature.cpp
index 1c98a3b..9b8fcd7 100644
--- a/src/osgEarthDrivers/kml/KML_Feature.cpp
+++ b/src/osgEarthDrivers/kml/KML_Feature.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Folder b/src/osgEarthDrivers/kml/KML_Folder
index 6463a2e..ebc4d47 100644
--- a/src/osgEarthDrivers/kml/KML_Folder
+++ b/src/osgEarthDrivers/kml/KML_Folder
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Folder.cpp b/src/osgEarthDrivers/kml/KML_Folder.cpp
index b5cde3b..49560cc 100644
--- a/src/osgEarthDrivers/kml/KML_Folder.cpp
+++ b/src/osgEarthDrivers/kml/KML_Folder.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Geometry b/src/osgEarthDrivers/kml/KML_Geometry
index bb4ad28..399dfff 100644
--- a/src/osgEarthDrivers/kml/KML_Geometry
+++ b/src/osgEarthDrivers/kml/KML_Geometry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Geometry.cpp b/src/osgEarthDrivers/kml/KML_Geometry.cpp
index 6432be6..538e0c9 100644
--- a/src/osgEarthDrivers/kml/KML_Geometry.cpp
+++ b/src/osgEarthDrivers/kml/KML_Geometry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_GroundOverlay b/src/osgEarthDrivers/kml/KML_GroundOverlay
index 6439f08..ef2769a 100644
--- a/src/osgEarthDrivers/kml/KML_GroundOverlay
+++ b/src/osgEarthDrivers/kml/KML_GroundOverlay
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp b/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp
index dfe8b5d..bf8c9ed 100644
--- a/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_IconStyle b/src/osgEarthDrivers/kml/KML_IconStyle
index 30dea09..87cc0bf 100644
--- a/src/osgEarthDrivers/kml/KML_IconStyle
+++ b/src/osgEarthDrivers/kml/KML_IconStyle
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_IconStyle.cpp b/src/osgEarthDrivers/kml/KML_IconStyle.cpp
index 8b5ad71..b11f57d 100644
--- a/src/osgEarthDrivers/kml/KML_IconStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_IconStyle.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LabelStyle b/src/osgEarthDrivers/kml/KML_LabelStyle
index 8f619c6..be05313 100644
--- a/src/osgEarthDrivers/kml/KML_LabelStyle
+++ b/src/osgEarthDrivers/kml/KML_LabelStyle
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LabelStyle.cpp b/src/osgEarthDrivers/kml/KML_LabelStyle.cpp
index 807492d..20c3bbb 100644
--- a/src/osgEarthDrivers/kml/KML_LabelStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_LabelStyle.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LineString b/src/osgEarthDrivers/kml/KML_LineString
index ca2bdc3..9b58367 100644
--- a/src/osgEarthDrivers/kml/KML_LineString
+++ b/src/osgEarthDrivers/kml/KML_LineString
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LineString.cpp b/src/osgEarthDrivers/kml/KML_LineString.cpp
index 5e37a34..d624961 100644
--- a/src/osgEarthDrivers/kml/KML_LineString.cpp
+++ b/src/osgEarthDrivers/kml/KML_LineString.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LineStyle b/src/osgEarthDrivers/kml/KML_LineStyle
index 35a6f5b..6cda17c 100644
--- a/src/osgEarthDrivers/kml/KML_LineStyle
+++ b/src/osgEarthDrivers/kml/KML_LineStyle
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LineStyle.cpp b/src/osgEarthDrivers/kml/KML_LineStyle.cpp
index 743d9c8..449f99a 100644
--- a/src/osgEarthDrivers/kml/KML_LineStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_LineStyle.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LinearRing b/src/osgEarthDrivers/kml/KML_LinearRing
index 3344de7..59bd22e 100644
--- a/src/osgEarthDrivers/kml/KML_LinearRing
+++ b/src/osgEarthDrivers/kml/KML_LinearRing
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_LinearRing.cpp b/src/osgEarthDrivers/kml/KML_LinearRing.cpp
index fc34a27..0efde1f 100644
--- a/src/osgEarthDrivers/kml/KML_LinearRing.cpp
+++ b/src/osgEarthDrivers/kml/KML_LinearRing.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Model b/src/osgEarthDrivers/kml/KML_Model
index 39156c1..75f2866 100644
--- a/src/osgEarthDrivers/kml/KML_Model
+++ b/src/osgEarthDrivers/kml/KML_Model
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Model.cpp b/src/osgEarthDrivers/kml/KML_Model.cpp
index 9638fab..079d536 100644
--- a/src/osgEarthDrivers/kml/KML_Model.cpp
+++ b/src/osgEarthDrivers/kml/KML_Model.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_MultiGeometry b/src/osgEarthDrivers/kml/KML_MultiGeometry
index 6bda743..38b43ac 100644
--- a/src/osgEarthDrivers/kml/KML_MultiGeometry
+++ b/src/osgEarthDrivers/kml/KML_MultiGeometry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp b/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp
index 98bc3de..e501979 100644
--- a/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp
+++ b/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLink.cpp b/src/osgEarthDrivers/kml/KML_NetworkLink.cpp
index b456e0a..39e5634 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLink.cpp
+++ b/src/osgEarthDrivers/kml/KML_NetworkLink.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -47,17 +47,21 @@ KML_NetworkLink::build( const Config& conf, KMLContext& cx )
         if ( llaBoxConf.empty() )
             return;
 
+        const SpatialReference* geoSRS = cx._mapNode->getMapSRS()->getGeographicSRS();
+
         GeoExtent llaExtent(
-            cx._mapNode->getMap()->getProfile()->getSRS()->getGeographicSRS(),
+            geoSRS,
             llaBoxConf.value<double>("west",  0.0),
             llaBoxConf.value<double>("south", 0.0),
             llaBoxConf.value<double>("east",  0.0),
             llaBoxConf.value<double>("north", 0.0) );
 
+        // find the ECEF LOD center point:
         double x, y;
         llaExtent.getCentroid( x, y );
         osg::Vec3d lodCenter;
-        llaExtent.getSRS()->transformToECEF( osg::Vec3d(x,y,0), lodCenter );
+        llaExtent.getSRS()->transform( osg::Vec3d(x,y,0), geoSRS->getECEF(), lodCenter );
+        //llaExtent.getSRS()->transformToECEF( osg::Vec3d(x,y,0), lodCenter );
 
         // figure the tile radius:
         double d = 0.5 * GeoMath::distance(
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLinkControl b/src/osgEarthDrivers/kml/KML_NetworkLinkControl
index e906595..0874189 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLinkControl
+++ b/src/osgEarthDrivers/kml/KML_NetworkLinkControl
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp b/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp
index b27bdca..ae98ad2 100644
--- a/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp
+++ b/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Object b/src/osgEarthDrivers/kml/KML_Object
index 63c52ef..6ec22b0 100644
--- a/src/osgEarthDrivers/kml/KML_Object
+++ b/src/osgEarthDrivers/kml/KML_Object
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Object.cpp b/src/osgEarthDrivers/kml/KML_Object.cpp
index 2b97a4c..48589f3 100644
--- a/src/osgEarthDrivers/kml/KML_Object.cpp
+++ b/src/osgEarthDrivers/kml/KML_Object.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Overlay b/src/osgEarthDrivers/kml/KML_Overlay
index 77f943c..e4669ad 100644
--- a/src/osgEarthDrivers/kml/KML_Overlay
+++ b/src/osgEarthDrivers/kml/KML_Overlay
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Overlay.cpp b/src/osgEarthDrivers/kml/KML_Overlay.cpp
index b1beb5d..cb00a3a 100644
--- a/src/osgEarthDrivers/kml/KML_Overlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_Overlay.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_PhotoOverlay b/src/osgEarthDrivers/kml/KML_PhotoOverlay
index a35cd32..cdf0d7f 100644
--- a/src/osgEarthDrivers/kml/KML_PhotoOverlay
+++ b/src/osgEarthDrivers/kml/KML_PhotoOverlay
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp b/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp
index d856c6f..26abdd4 100644
--- a/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Placemark.cpp b/src/osgEarthDrivers/kml/KML_Placemark.cpp
index 8e2c69b..60a05db 100644
--- a/src/osgEarthDrivers/kml/KML_Placemark.cpp
+++ b/src/osgEarthDrivers/kml/KML_Placemark.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Point b/src/osgEarthDrivers/kml/KML_Point
index b3a5b81..c3a762f 100644
--- a/src/osgEarthDrivers/kml/KML_Point
+++ b/src/osgEarthDrivers/kml/KML_Point
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Point.cpp b/src/osgEarthDrivers/kml/KML_Point.cpp
index 944dc6c..6ead7f9 100644
--- a/src/osgEarthDrivers/kml/KML_Point.cpp
+++ b/src/osgEarthDrivers/kml/KML_Point.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_PolyStyle b/src/osgEarthDrivers/kml/KML_PolyStyle
index 04907f8..264c43d 100644
--- a/src/osgEarthDrivers/kml/KML_PolyStyle
+++ b/src/osgEarthDrivers/kml/KML_PolyStyle
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_PolyStyle.cpp b/src/osgEarthDrivers/kml/KML_PolyStyle.cpp
index a3fc042..ccb1710 100644
--- a/src/osgEarthDrivers/kml/KML_PolyStyle.cpp
+++ b/src/osgEarthDrivers/kml/KML_PolyStyle.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Polygon b/src/osgEarthDrivers/kml/KML_Polygon
index 781f6c4..bc52885 100644
--- a/src/osgEarthDrivers/kml/KML_Polygon
+++ b/src/osgEarthDrivers/kml/KML_Polygon
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Polygon.cpp b/src/osgEarthDrivers/kml/KML_Polygon.cpp
index b70a978..9273db2 100644
--- a/src/osgEarthDrivers/kml/KML_Polygon.cpp
+++ b/src/osgEarthDrivers/kml/KML_Polygon.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Root b/src/osgEarthDrivers/kml/KML_Root
index 0ef7dee..6a84b27 100644
--- a/src/osgEarthDrivers/kml/KML_Root
+++ b/src/osgEarthDrivers/kml/KML_Root
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Root.cpp b/src/osgEarthDrivers/kml/KML_Root.cpp
index a80d0ee..3bbfb67 100644
--- a/src/osgEarthDrivers/kml/KML_Root.cpp
+++ b/src/osgEarthDrivers/kml/KML_Root.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Schema b/src/osgEarthDrivers/kml/KML_Schema
index 1d79094..e1641c7 100644
--- a/src/osgEarthDrivers/kml/KML_Schema
+++ b/src/osgEarthDrivers/kml/KML_Schema
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Schema.cpp b/src/osgEarthDrivers/kml/KML_Schema.cpp
index 939c1e6..c58cadd 100644
--- a/src/osgEarthDrivers/kml/KML_Schema.cpp
+++ b/src/osgEarthDrivers/kml/KML_Schema.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_ScreenOverlay b/src/osgEarthDrivers/kml/KML_ScreenOverlay
index 6adb5e3..08cab36 100644
--- a/src/osgEarthDrivers/kml/KML_ScreenOverlay
+++ b/src/osgEarthDrivers/kml/KML_ScreenOverlay
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp b/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp
index e717a98..62836b2 100644
--- a/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp
+++ b/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Style b/src/osgEarthDrivers/kml/KML_Style
index 1a8d156..2c12bcc 100644
--- a/src/osgEarthDrivers/kml/KML_Style
+++ b/src/osgEarthDrivers/kml/KML_Style
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_Style.cpp b/src/osgEarthDrivers/kml/KML_Style.cpp
index 4d2c645..b6b9bab 100644
--- a/src/osgEarthDrivers/kml/KML_Style.cpp
+++ b/src/osgEarthDrivers/kml/KML_Style.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_StyleMap b/src/osgEarthDrivers/kml/KML_StyleMap
index 3b421c1..a3c9dfd 100644
--- a/src/osgEarthDrivers/kml/KML_StyleMap
+++ b/src/osgEarthDrivers/kml/KML_StyleMap
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_StyleMap.cpp b/src/osgEarthDrivers/kml/KML_StyleMap.cpp
index 7e5f308..7a7359e 100644
--- a/src/osgEarthDrivers/kml/KML_StyleMap.cpp
+++ b/src/osgEarthDrivers/kml/KML_StyleMap.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KML_StyleSelector b/src/osgEarthDrivers/kml/KML_StyleSelector
index 286ae6e..6b998bf 100644
--- a/src/osgEarthDrivers/kml/KML_StyleSelector
+++ b/src/osgEarthDrivers/kml/KML_StyleSelector
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KMZArchive b/src/osgEarthDrivers/kml/KMZArchive
index bd1b46e..f617953 100644
--- a/src/osgEarthDrivers/kml/KMZArchive
+++ b/src/osgEarthDrivers/kml/KMZArchive
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/KMZArchive.cpp b/src/osgEarthDrivers/kml/KMZArchive.cpp
index 49c0509..2358213 100644
--- a/src/osgEarthDrivers/kml/KMZArchive.cpp
+++ b/src/osgEarthDrivers/kml/KMZArchive.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/kml/ReaderWriterKML.cpp b/src/osgEarthDrivers/kml/ReaderWriterKML.cpp
index 66a8d19..730de75 100644
--- a/src/osgEarthDrivers/kml/ReaderWriterKML.cpp
+++ b/src/osgEarthDrivers/kml/ReaderWriterKML.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/label_annotation/AnnotationLabelSource.cpp b/src/osgEarthDrivers/label_annotation/AnnotationLabelSource.cpp
index a5e450e..fddeb41 100644
--- a/src/osgEarthDrivers/label_annotation/AnnotationLabelSource.cpp
+++ b/src/osgEarthDrivers/label_annotation/AnnotationLabelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -63,18 +63,24 @@ public:
 
         osg::Group* group = new osg::Group();
 
+#if 0
+        //TODO: revise; decluttering is enabled by the LabelNode now -gw
+
         // check for decluttering
         if ( text->declutter().isSet() )
         {
             Decluttering::setEnabled( group->getOrCreateStateSet(), *text->declutter() );
         }
+#endif
 
+#if 0
         if ( text->priority().isSet() )
         {
             DeclutteringOptions dco = Decluttering::getOptions();
             dco.sortByPriority() = text->priority().isSet();
             Decluttering::setOptions( dco );
-        }    
+        }
+#endif
         
         StringExpression  contentExpr ( *text->content() );
         NumericExpression priorityExpr( *text->priority() );
diff --git a/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp b/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp
index 9e55c33..a28fb24 100644
--- a/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp
+++ b/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -99,10 +99,12 @@ public:
         StringExpression  contentExpr ( *text->content() );
         NumericExpression priorityExpr( *text->priority() );
 
-        bool makeECEF = false;
+        //bool makeECEF = false;
+        const SpatialReference* ecef = 0L;
         if ( context.isGeoreferenced() )
         {
-            makeECEF = context.getSession()->getMapInfo().isGeocentric();
+            //makeECEF = context.getSession()->getMapInfo().isGeocentric();
+            ecef = context.getSession()->getMapSRS()->getECEF();
         }
 
         for( FeatureList::const_iterator i = input.begin(); i != input.end(); ++i )
@@ -117,9 +119,10 @@ public:
 
             osg::Vec3d centroid  = geom->getBounds().center();
 
-            if ( makeECEF )
+            if ( ecef )
             {
-                context.profile()->getSRS()->transformToECEF( centroid, centroid );
+                context.profile()->getSRS()->transform( centroid, ecef, centroid );
+                //context.profile()->getSRS()->transformToECEF( centroid, centroid );
             }
 
             const std::string& value = feature->eval( contentExpr, &context );
@@ -149,11 +152,12 @@ public:
                 xform->addChild( node );
 
                 // for a geocentric map, do a simple dot product cull.
-                if ( makeECEF )
+                if ( ecef )
                 {
                     xform->setCullCallback( new CullNodeByHorizon(
                         centroid, 
-                        context.getSession()->getMapInfo().getProfile()->getSRS()->getEllipsoid()) );
+                        ecef->getEllipsoid() ) );
+                        //context.getSession()->getMapSRS()->getEllipsoid() ) ); //getMapInfo().getProfile()->getSRS()->getEllipsoid()) );
                     group->addChild( xform );
                 }
                 else
diff --git a/src/osgEarthDrivers/mask_feature/FeatureMaskOptions b/src/osgEarthDrivers/mask_feature/FeatureMaskOptions
index 39a969f..03ee435 100644
--- a/src/osgEarthDrivers/mask_feature/FeatureMaskOptions
+++ b/src/osgEarthDrivers/mask_feature/FeatureMaskOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp b/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp
index d68205c..a40bc77 100644
--- a/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp
+++ b/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions b/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions
index 7481d99..095bccb 100644
--- a/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions
+++ b/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp b/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp
index 454e894..494d94d 100644
--- a/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp
+++ b/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelOptions b/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelOptions
index ae50e96..45038b7 100644
--- a/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelOptions
+++ b/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp b/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp
index b39e52b..7bd035a 100644
--- a/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp
+++ b/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/model_simple/SimpleModelOptions b/src/osgEarthDrivers/model_simple/SimpleModelOptions
index 9c44e5c..c3232e5 100644
--- a/src/osgEarthDrivers/model_simple/SimpleModelOptions
+++ b/src/osgEarthDrivers/model_simple/SimpleModelOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp b/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp
index e4cb34d..d22eb2d 100644
--- a/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp
+++ b/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -84,7 +84,7 @@ public:
     }
 
     // override
-    osg::Node* createNode(const Map* map, const osgDB::Options* dbOptions, ProgressCallback* progress )
+    osg::Node* createNodeImplementation(const Map* map, const osgDB::Options* dbOptions, ProgressCallback* progress )
     {
         osg::ref_ptr<osg::Node> result;
 
diff --git a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer
index e889b36..89ee3dd 100644
--- a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer
+++ b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp
index 365841f..2286255 100644
--- a/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp
+++ b/src/osgEarthDrivers/ocean_surface/ElevationProxyImageLayer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/ocean_surface/OceanCompositor b/src/osgEarthDrivers/ocean_surface/OceanCompositor
index a00126f..685cb1b 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanCompositor
+++ b/src/osgEarthDrivers/ocean_surface/OceanCompositor
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
 #define OSGEARTH_DRIVER_OCEAN_SURFACE_COMPOSITOR 1
 
 #include <osgEarth/TextureCompositor>
+#include "OceanSurface"
 
 namespace osgEarth_ocean_surface
 {
@@ -31,7 +32,7 @@ namespace osgEarth_ocean_surface
     class OceanCompositor : public TextureCompositorTechnique
     {
     public:
-        OceanCompositor() { }
+        OceanCompositor(const osgEarth::Drivers::OceanSurfaceOptions& options);
 
         /** dtor */
         virtual ~OceanCompositor() { }
@@ -48,6 +49,10 @@ namespace osgEarth_ocean_surface
                                       const TileKey&       tileKey,
                                       const TextureLayout& layout,
                                       osg::StateSet*       parentStateSet) const;
+
+    private:
+
+        osgEarth::Drivers::OceanSurfaceOptions _options;
     };
 
 } // namespace osgEarth_ocean_surface
diff --git a/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp b/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp
index ae87dc7..792cb35 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp
+++ b/src/osgEarthDrivers/ocean_surface/OceanCompositor.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,11 +20,23 @@
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/VirtualProgram>
+#include <osgEarth/ShaderFactory>
 #include <osg/Texture2D>
 #include "OceanShaders"
 
+#define OCEAN_DATA "ocean_data"
+#define OCEAN_TEX  "ocean_surface_tex"
+
 using namespace osgEarth_ocean_surface;
 using namespace osgEarth;
+using namespace osgEarth::Drivers;
+
+
+OceanCompositor::OceanCompositor(const OceanSurfaceOptions& options) :
+_options( options )
+{
+    //nop
+}
 
 void
 OceanCompositor::updateMasterStateSet(osg::StateSet*       stateSet, 
@@ -38,28 +50,41 @@ OceanCompositor::updateMasterStateSet(osg::StateSet*       stateSet,
         stateSet->setAttributeAndModes( vp, 1 );
     }
     
-    vp->installDefaultLightingShaders();
-    
-    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 );
+    //vp->installDefaultLightingShaders();
+    Registry::shaderFactory()->installLightingShaders( vp );
+
+    // use the appropriate shader for the active technique:
+    std::string vertSource = _options.maskLayer().isSet() ? source_vertMask : source_vertProxy;
+    std::string fragSource = _options.maskLayer().isSet() ? source_fragMask : source_fragProxy;
+
+    vp->setFunction( "oe_ocean_vertex",   vertSource, ShaderComp::LOCATION_VERTEX_VIEW );
+
+    vp->setFunction( "oe_ocean_fragment", fragSource, ShaderComp::LOCATION_FRAGMENT_COLORING );
+
+    //vp->setShader( 
+    //    "osgearth_vert_setupColoring", 
+    //    new osg::Shader(osg::Shader::VERTEX, vertSource),
+    //    osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+
+    //vp->setShader( 
+    //    "osgearth_frag_applyColoring", 
+    //    new osg::Shader(osg::Shader::FRAGMENT, fragSource),
+    //    osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
+
+    // install the slot attribute(s)
+    stateSet->getOrCreateUniform( OCEAN_DATA, osg::Uniform::SAMPLER_2D )->set( 0 );
+    stateSet->getOrCreateUniform( OCEAN_TEX,  osg::Uniform::SAMPLER_2D )->set( 1 );
 }
-    
+
 namespace
 {
+    // probably don't need this
     std::string makeSamplerName(int slot)
     {
-        std::stringstream buf;
-        buf << "ocean_tex" << slot;
-        std::string str;
-        str = buf.str();
-        return str;
+        if ( slot == 0 )
+            return OCEAN_DATA;
+        else 
+            return OCEAN_TEX;
     }
 
     osg::Texture2D*
@@ -85,10 +110,6 @@ namespace
             tex->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
 
             stateSet->setTextureAttributeAndModes( slot, tex, osg::StateAttribute::ON );
-            
-            // install the slot attribute
-            std::string name = makeSamplerName( slot );
-            stateSet->getOrCreateUniform( name.c_str(), osg::Uniform::SAMPLER_2D )->set( slot );
         }
         return tex;
     }
diff --git a/src/osgEarthDrivers/ocean_surface/OceanShaders b/src/osgEarthDrivers/ocean_surface/OceanShaders
index 48d13e4..c813c33 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanShaders
+++ b/src/osgEarthDrivers/ocean_surface/OceanShaders
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -21,9 +21,12 @@
 
 namespace
 {
-    static char source_setupColoring[] =
+    static char source_vertProxy[] =
 
         "#version " GLSL_VERSION_STR "\n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float;\n"
+#endif
 
         "vec2 ocean_xyz_to_spherical(in vec3 xyz) \n"
         "{ \n"
@@ -35,29 +38,31 @@ namespace
 
         "uniform mat4 osg_ViewMatrixInverse; \n"
         "uniform mat4 osg_ViewMatrix; \n"
-        "uniform sampler2D ocean_tex0; \n"                 // heightfield encoded into 16 bit texture
+        "uniform float osg_FrameTime; \n"
+        "uniform sampler2D ocean_data; \n"                 // heightfield encoded into 16 bit texture
         "uniform float ocean_seaLevel; \n"                 // sea level offset
-        "uniform bool ocean_has_tex1; \n"                  // whether there's a surface texture
 
-        "varying vec4  osg_FrontColor; \n"
-        "varying vec2  ocean_texCoord1; \n"
+        "varying vec4 osg_FrontColor; \n"
+        "varying vec4 ocean_surface_tex_coord; \n"
         "varying float ocean_v_msl; \n"                    // elevation (MSL) of camera
         "varying float ocean_v_range; \n"                  // distance from camera to current vertex
         "varying float ocean_v_enorm; \n"                  // normalized terrain height at vertex [0..1]
 
-        "void osgearth_vert_setupColoring() \n"
+        "void oe_ocean_vertex(inout vec4 VertexMODEL) \n"
         "{ \n"
         "   osg_FrontColor = gl_Color; \n"
 
         // adjust our vert for the sea level - extrude along the normal vector 
         // (this must be done in modelview space to preserve precision)
-        "   vec4 mvVertex = gl_ModelViewMatrix * gl_Vertex; \n"
+        "   vec4 mvVertex = VertexMODEL; \n"
         "   vec3 mvNormal = gl_NormalMatrix * gl_Normal; \n"
         "   vec4 mvVertex2 = vec4(mvVertex.xyz + (mvNormal * ocean_seaLevel), mvVertex.w ); \n"
-        "   gl_Position = gl_ProjectionMatrix * mvVertex2; \n"
+
+        "   VertexMODEL = mvVertex2; \n"
+        //"   gl_Position = gl_ProjectionMatrix * mvVertex2; \n"
 
         // read normalized [0..1] elevation data from the height texture:
-        "   ocean_v_enorm = texture2D( ocean_tex0, gl_MultiTexCoord0.st ).r; \n"
+        "   ocean_v_enorm = texture2D( ocean_data, gl_MultiTexCoord0.st ).r; \n"
 
         // send interpolated params to the fs:
         "   vec4 eye = osg_ViewMatrixInverse * vec4(0,0,0,1); \n"
@@ -66,18 +71,24 @@ namespace
         "   ocean_v_msl = length(eye.xyz/eye.w) - 6378137.0 + ocean_seaLevel; \n"
 
         // disatnce to camera:
-        "   ocean_v_range = length(gl_Position); \n"
+        "   ocean_v_range = ocean_v_msl; \n"
 
         // scale the texture mapping to something reasonable:
         "   vec4 worldVertex = osg_ViewMatrixInverse * mvVertex; \n"
         "   vec2 lonlat = ocean_xyz_to_spherical( worldVertex.xyz/worldVertex.w ); \n"
-        "   ocean_texCoord1 = lonlat / 0.0005; \n"
+        "   ocean_surface_tex_coord.xy = lonlat / 0.0005; \n"
+        "   ocean_surface_tex_coord.zw = ocean_surface_tex_coord.xy; \n"
+        "   ocean_surface_tex_coord.x += mod(osg_FrameTime,100.0)/100.0;\n"
+        "   ocean_surface_tex_coord.w -= mod(osg_FrameTime,25.0)/25.0;\n"
         "} \n";
 
 
-    char source_applyColoring[] = 
+    char source_fragProxy[] = 
 
         "#version " GLSL_VERSION_STR "\n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float;\n"
+#endif
 
         // clamps a value to the vmin/vmax range, then re-maps it to the r0/r1 range:
         "float ocean_remap( float val, float vmin, float vmax, float r0, float r1 ) \n"
@@ -89,16 +100,19 @@ namespace
         "varying float ocean_v_msl; \n"                  // elevation (MSL) of camera
         "varying float ocean_v_range; \n"                // distance from camera to current vertex
         "varying float ocean_v_enorm; \n"                // normalized terrain height at vertex [0..1]
-        "varying vec2  ocean_texCoord1; \n"              // surface texture coords
+        "varying vec4 ocean_surface_tex_coord; \n"       // surface texture coords
 
-        "uniform bool ocean_has_tex1; \n"                // whether there's a surface texture
-        "uniform sampler2D ocean_tex1; \n"               // surface texture
+        "uniform bool ocean_has_surface_tex; \n"         // whether there's a surface texture
+        "uniform sampler2D ocean_surface_tex; \n"        // surface texture
         "uniform float ocean_seaLevel; \n"               // sea level offset
         "uniform float ocean_lowFeather; \n"             // offset from sea level at which to start feathering out
         "uniform float ocean_highFeather; \n"            // offset from sea level at which to stop feathering out
         "uniform vec4  ocean_baseColor; \n"              // base ocean color before processing
 
-        "void osgearth_frag_applyColoring(inout vec4 color) \n"
+        "uniform float ocean_max_range; \n"              // maximum visible distance of the ocean
+        "uniform float ocean_fade_range; \n"             // distance over which to fade in the ocean
+
+        "void oe_ocean_fragment(inout vec4 color) \n"
         "{ \n"
         "    color = ocean_baseColor; \n"
 
@@ -106,12 +120,17 @@ namespace
         "    float rangeFactor = ocean_remap( ocean_v_msl, -10000.0, 10000.0, 10.0, 1.0 ); \n"
 
         // affect alpha based on the distance from the camera
-        "    float rangeEffect = ocean_remap( ocean_v_range, 75000.0, 200000.0 * rangeFactor, 1.0, 0.0 ); \n"
+        "    float rangeEffect = ocean_remap(\n"
+        "       ocean_v_range,\n"
+        "       ocean_max_range - ocean_fade_range, ocean_max_range * rangeFactor,\n"
+        "       1.0, 0.0); \n"
 
         // start with the surface texture.
-        "    if (ocean_has_tex1) \n"
+        "    if (ocean_has_surface_tex) \n"
         "    { \n"
-        "        vec4 texel = texture2D(ocean_tex1, ocean_texCoord1); \n"
+        "        vec4 texel1 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.xy); \n"
+        "        vec4 texel2 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.zw); \n"
+        "        vec4 texel  = vec4(texel1.rgb*texel2.rgb, texel2.a); \n"
         "        vec4 nearColor = vec4(mix(color.rgb, texel.rgb, texel.a), color.a); \n"
         "        color = mix(color, nearColor, rangeEffect); \n"
         "    } \n"
@@ -122,17 +141,136 @@ namespace
         // heightfield's effect on alpha [0..1]
         "    float terrainEffect = ocean_remap( terrainHeight, ocean_seaLevel+ocean_lowFeather, ocean_seaLevel+ocean_highFeather, 1.0, 0.0 ); \n" 
 
-        // balance between texture-based alpha and static alpha
-        //"    float texBalance = remap( v_range, 7500.0, 15000.0, 0.0, 1.0 ); \n"
-        //"    float texIntensity = texture2D( tex1, texCoord1 ).r; \n"
-        //"    float texEffect = mix( texIntensity, 0.8, texBalance ); \n"
-
         // color it
         "    color = vec4( color.rgb, terrainEffect * rangeEffect * color.a ); \n"
 
         //"    color = vec4( 1, 0, 0, 1 ); \n" // debugging
         "} \n";
 
+
+
+    char source_vertMask[] =
+
+        "#version " GLSL_VERSION_STR "\n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float;\n"
+#endif
+
+        "vec2 ocean_xyz_to_spherical(in vec3 xyz) \n"
+        "{ \n"
+        "    float r = length(xyz); \n"
+        "    float lat = acos(xyz.z/r); \n"
+        "    float lon = atan(xyz.y, xyz.x); \n"
+        "    return vec2(lon,lat); \n"
+        "} \n"
+
+        "uniform mat4 osg_ViewMatrixInverse; \n"
+        "uniform float osg_FrameTime; \n"
+        "uniform float ocean_seaLevel; \n"
+        "varying vec4 osg_FrontColor; \n"
+        "varying vec4 ocean_mask_tex_coord; \n"
+        "varying vec4 ocean_surface_tex_coord; \n"
+        "varying float ocean_v_msl; \n"
+        "varying float ocean_v_range; \n"
+
+        "void oe_ocean_vertex(inout vec4 VertexMODEL) \n"
+        "{ \n"
+        "   osg_FrontColor = gl_Color; \n"
+
+        // adjust our vert for the sea level - extrude along the normal vector 
+        // (this must be done in modelview space to preserve precision)
+        "   vec4 mvVertex = VertexMODEL; \n"
+        "   vec3 mvNormal = gl_NormalMatrix * gl_Normal; \n"
+        "   vec4 mvVertex2 = vec4(mvVertex.xyz + (mvNormal * ocean_seaLevel), mvVertex.w ); \n"
+
+        "   VertexMODEL = mvVertex2; \n"
+        //"   gl_Position = gl_ProjectionMatrix * mvVertex2; \n"
+
+        // ocean mask texture coordinate:
+        "   ocean_mask_tex_coord = gl_MultiTexCoord0; \n"
+
+        // send interpolated params to the fs:
+        "   vec4 eye = osg_ViewMatrixInverse * vec4(0,0,0,1); \n"
+
+        // height of camera above sea level:
+        "   ocean_v_msl = length(eye.xyz/eye.w) - 6378137.0 + ocean_seaLevel; \n"
+
+        // disatnce to camera:
+        "   ocean_v_range = ocean_v_msl; \n"
+
+        // scale the texture mapping to something reasonable:
+        "   vec4 worldVertex = osg_ViewMatrixInverse * mvVertex; \n"
+        "   vec2 lonlat = ocean_xyz_to_spherical( worldVertex.xyz/worldVertex.w ); \n"
+        "   ocean_surface_tex_coord.xy = lonlat / 0.0005; \n"
+        "   ocean_surface_tex_coord.zw = ocean_surface_tex_coord.xy; \n"
+        "   ocean_surface_tex_coord.x += mod(osg_FrameTime,100.0)/100.0;\n"
+        "   ocean_surface_tex_coord.w -= mod(osg_FrameTime,50.0)/50.0;\n"
+        "} \n";
+
+
+    char source_fragMask[] = 
+
+        "#version " GLSL_VERSION_STR "\n"
+#ifdef OSG_GLES2_AVAILABLE
+        "precision mediump float;\n"
+#endif
+
+        // clamps a value to the vmin/vmax range, then re-maps it to the r0/r1 range:
+        "float ocean_remap( float val, float vmin, float vmax, float r0, float r1 ) \n"
+        "{ \n"
+        "    float vr = (clamp(val, vmin, vmax)-vmin)/(vmax-vmin); \n"
+        "    return r0 + vr * (r1-r0); \n"
+        "} \n"
+
+        "varying float ocean_v_msl; \n"                  // elevation (MSL) of camera
+        "varying float ocean_v_range; \n"                // distance from camera to current vertex
+        "varying vec4 ocean_mask_tex_coord; \n"          // tex coord for the mask texture
+        "varying vec4 ocean_surface_tex_coord; \n"       // surface texture coords
+
+        "uniform sampler2D ocean_data; \n"               // ocean mask texture
+        "uniform bool ocean_has_surface_tex; \n"         // whether there's a surface texture
+        "uniform sampler2D ocean_surface_tex; \n"        // surface texture
+
+        "uniform float ocean_seaLevel; \n"               // sea level offset
+        "uniform vec4 ocean_baseColor; \n"               // base ocean color before processing
+
+        "uniform float ocean_max_range; \n"              // maximum visible distance of the ocean
+        "uniform float ocean_fade_range; \n"             // distance over which to fade in the ocean
+
+        "void oe_ocean_fragment(inout vec4 color) \n"
+        "{ \n"
+        "    color = ocean_baseColor; \n"
+
+        // amplify the range's effect on alpha when the camera elevation gets low
+        "    float rangeFactor = ocean_remap( ocean_v_msl, -10000.0, 10000.0, 10.0, 1.0 ); \n"
+
+        // affect alpha based on the distance from the camera
+        "    float rangeEffect = ocean_remap(\n"
+        "       ocean_v_range,\n"
+        "       ocean_max_range - ocean_fade_range, ocean_max_range * rangeFactor,\n"
+        "       1.0, 0.0); \n"
+
+        // start with the surface texture.
+        "    if (ocean_has_surface_tex) \n"
+        "    { \n"
+        "        vec4 texel1 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.xy); \n"
+        "        vec4 texel2 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.zw); \n"
+        "        vec4 texel  = vec4(texel1.rgb*texel2.rgb, texel2.a); \n"
+        "        vec4 nearColor = vec4(mix(color.rgb, texel.rgb, texel.a), color.a); \n"
+        "        color = mix(color, nearColor, rangeEffect); \n"
+        "    } \n"
+
+        // effect of the terrain mask [0..1] in the alpha component.
+        "    float maskEffect = texture2D(ocean_data, ocean_mask_tex_coord.xy).a; \n"
+
+        // color it
+        "    color = vec4( color.rgb, maskEffect * rangeEffect * color.a ); \n"
+
+        //"    color = vec4( 1, 0, 0, 1 ); \n" // debugging
+        "} \n";
+
+
+
 }
 
 #endif // OSGEARTH_DRIVER_OCEAN_SURFACE_SHADERS
diff --git a/src/osgEarthDrivers/ocean_surface/OceanSurface b/src/osgEarthDrivers/ocean_surface/OceanSurface
index 2572413..f7f534a 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanSurface
+++ b/src/osgEarthDrivers/ocean_surface/OceanSurface
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -44,11 +44,13 @@ namespace osgEarth { namespace Drivers
         optional<float>& seaLevel() { return _seaLevel; }
         const optional<float>& seaLevel() const { return _seaLevel; }
 
-        /** Elevation offset (relative to seaLevel) at which ocean surface goes to min transparency */
+        /** Elevation offset (relative to seaLevel) at which ocean surface goes to min transparency.
+         *  Not available when using a mask layer. */
         optional<float>& lowFeatherOffset() { return _lowFeatherOffset; }
         const optional<float>& lowFeatherOffset() const { return _lowFeatherOffset; }
 
-        /** Elevation offset (relative to seaLevel) at which ocean surface goes to full transparency */
+        /** Elevation offset (relative to seaLevel) at which ocean surface goes to full transparency.
+         *  Not available when using a mask layer. */
         optional<float>& highFeatherOffset() { return _highFeatherOffset; }
         const optional<float>& highFeatherOffset() const { return _highFeatherOffset; }
 
@@ -56,20 +58,36 @@ namespace osgEarth { namespace Drivers
         optional<unsigned>& maxLOD() { return _maxLOD; }
         const optional<unsigned>& maxLOD() const { return _maxLOD; }
 
-        /** Base color of the ocean surface */
+        /** Color of the ocean surface (before texturing) */
         optional<Color>& baseColor() { return _baseColor; }
         const optional<Color>& baseColor() const { return _baseColor; }
 
-        /** URI of the intensity texture to use for the ocean surface alpha. */
+        /** Maximum visibile range of the ocean (at which is starts fading in) */
+        optional<float>& maxRange() { return _maxRange; }
+        const optional<float>& maxRange() const { return _maxRange; }
+
+        /** Range over which ocean fades into view (starting at maxRange) */
+        optional<float>& fadeRange() { return _fadeRange; }
+        const optional<float>& fadeRange() const { return _fadeRange; }
+
+        /** URI of the texture to blend and animate into the ocean surface. */
         optional<URI>& textureURI() { return _textureURI; }
         const optional<URI>& textureURI() const { return _textureURI; }
 
+        /** Image layer configuration for an optional "ocean mask" layer.
+         *  This is an image layer that encodes areas of land versus ocean in the
+         *  alpha channel of the image. The mapping is: [0...1] => [land...ocean] */
+        optional<ImageLayerOptions>& maskLayer() { return _maskLayerOptions; }
+        const optional<ImageLayerOptions>& maskLayer() const { return _maskLayerOptions; }
+
     public:
         OceanSurfaceOptions( const Config& conf =Config() )
             : ConfigOptions     ( conf ),
               _seaLevel         ( 0.0f ),
               _lowFeatherOffset ( -100.0f ),
               _highFeatherOffset( -10.0f ),
+              _maxRange         ( 1000000.0f ),
+              _fadeRange        ( 225000.0f ),
               _maxLOD           ( 11 ),
               _baseColor        ( osg::Vec4(0.2, 0.3, 0.5, 0.8) )
         {
@@ -85,9 +103,12 @@ namespace osgEarth { namespace Drivers
             conf.updateIfSet("sea_level",           _seaLevel );
             conf.updateIfSet("high_feather_offset", _highFeatherOffset );
             conf.updateIfSet("low_feather_offset",  _lowFeatherOffset );
+            conf.updateIfSet("max_range",           _maxRange );
+            conf.updateIfSet("fade_range",          _fadeRange );
             conf.updateIfSet("max_lod",             _maxLOD );
             conf.updateIfSet("base_color",          _baseColor );
             conf.updateIfSet("texture_url",         _textureURI );
+            conf.updateObjIfSet("mask_layer",       _maskLayerOptions );
             return conf;
         }
 
@@ -102,18 +123,24 @@ namespace osgEarth { namespace Drivers
             conf.getIfSet("sea_level",           _seaLevel );
             conf.getIfSet("high_feather_offset", _highFeatherOffset );
             conf.getIfSet("low_feather_offset",  _lowFeatherOffset );
+            conf.getIfSet("max_range",           _maxRange );
+            conf.getIfSet("fade_range",          _fadeRange );
             conf.getIfSet("max_lod",             _maxLOD );
             conf.getIfSet("base_color",          _baseColor );
             conf.getIfSet("texture_url",         _textureURI );
+            conf.getObjIfSet("mask_layer",       _maskLayerOptions );
         }
 
     private:
-        optional<float>          _seaLevel;
-        optional<float>          _lowFeatherOffset;
-        optional<float>          _highFeatherOffset;
-        optional<unsigned>       _maxLOD;
-        optional<Color>          _baseColor;
-        optional<URI>            _textureURI;
+        optional<float>             _seaLevel;
+        optional<float>             _lowFeatherOffset;
+        optional<float>             _highFeatherOffset;
+        optional<float>             _maxRange;
+        optional<float>             _fadeRange;
+        optional<unsigned>          _maxLOD;
+        optional<Color>             _baseColor;
+        optional<URI>               _textureURI;
+        optional<ImageLayerOptions> _maskLayerOptions;
     };
 
 
@@ -159,7 +186,7 @@ namespace osgEarth { namespace Drivers
             osg::ref_ptr<MapNode> safeMapNode = _mapNode.get();
             if ( safeMapNode.valid() )
             {
-                osg::ref_ptr<osgDB::Options> o = Registry::instance()->cloneOrCreateOptions(); //new osgDB::Options();
+                osg::ref_ptr<osgDB::Options> o = Registry::instance()->cloneOrCreateOptions();
                 o->setPluginData( "mapNode", (void*)_mapNode.get() );
                 o->setPluginData( "options", (void*)&_options );
                 osgDB::ReaderWriter::ReadResult r = osgDB::readNodeFile( ".osgearth_ocean_surface", o.get() );
diff --git a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer
index 1f4f551..2393966 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer
+++ b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -52,6 +52,7 @@ namespace osgEarth_ocean_surface
         osg::observer_ptr<MapNode> _parentMapNode;
         OceanSurfaceOptions        _options;
         osg::ref_ptr<osg::Uniform> _seaLevel, _lowFeather, _highFeather;
+        osg::ref_ptr<osg::Uniform> _maxRange, _fadeRange;
         osg::ref_ptr<osg::Uniform> _baseColor;
 
         void rebuild();
diff --git a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp
index 1c61ef2..c95dd8a 100644
--- a/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp
+++ b/src/osgEarthDrivers/ocean_surface/OceanSurfaceContainer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -76,14 +76,34 @@ OceanSurfaceContainer::rebuild()
         MapNode* oceanMapNode = new MapNode( oceanMap, mno );
         
         // install a custom compositor. Must do this before adding any image layers.
-        oceanMapNode->setCompositorTechnique( new OceanCompositor() );
+        oceanMapNode->setCompositorTechnique( new OceanCompositor(_options) );
 
-        // 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) );
+        // if the caller requested a mask layer, install that now.
+        if ( _options.maskLayer().isSet() )
+        {
+            if ( !_options.maskLayer()->maxLevel().isSet() )
+            {
+                // set the max subdivision level if it's not already specified in the 
+                // mask layer options:
+                _options.maskLayer()->maxLevel() = *_options.maxLOD();
+            }
+
+            ImageLayer* maskLayer = new ImageLayer( "ocean-mask", *_options.maskLayer() );
+            oceanMap->addImageLayer( maskLayer );
+        }
+
+        // otherwise, install a "proxy layer" that will use the elevation data in the map
+        // to determine where the ocean is. This approach is limited in that it cannot
+        // detect the difference between ocean and inland areas that are below sea level.
+        else
+        {
+            // 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) );
+        }
 
         this->addChild( oceanMapNode );
 
@@ -102,12 +122,18 @@ OceanSurfaceContainer::rebuild()
         _baseColor = new osg::Uniform(osg::Uniform::FLOAT_VEC4, "ocean_baseColor");
         ss->addUniform( _baseColor.get() );
 
-        // trick to prevent z-fighting..
+        _maxRange = new osg::Uniform(osg::Uniform::FLOAT, "ocean_max_range");
+        ss->addUniform( _maxRange.get() );
+
+        _fadeRange = new osg::Uniform(osg::Uniform::FLOAT, "ocean_fade_range");
+        ss->addUniform( _fadeRange.get() );
+
+        // trick to mitigate z-fighting..
         ss->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0.0, 1.0, false) );
         ss->setRenderBinDetails( 15, "RenderBin" );
 
         // load up a surface texture
-        ss->getOrCreateUniform( "ocean_has_tex1", osg::Uniform::BOOL )->set( false );
+        ss->getOrCreateUniform( "ocean_has_surface_tex", osg::Uniform::BOOL )->set( false );
         if ( _options.textureURI().isSet() )
         {
             //TODO: enable cache support here:
@@ -121,14 +147,16 @@ OceanSurfaceContainer::rebuild()
                 tex->setWrap  ( osg::Texture::WRAP_T, osg::Texture::REPEAT );
 
                 ss->setTextureAttributeAndModes( 1, tex, 1 );
-                ss->getOrCreateUniform( "ocean_tex1", osg::Uniform::SAMPLER_2D )->set( 1 );
-                ss->getOrCreateUniform( "ocean_has_tex1", osg::Uniform::BOOL )->set( true );
+                ss->getOrCreateUniform( "ocean_surface_tex", osg::Uniform::SAMPLER_2D )->set( 1 );
+                ss->getOrCreateUniform( "ocean_has_surface_tex", osg::Uniform::BOOL )->set( true );
             }
         }
 
         // remove backface culling so we can see underwater
         // (use OVERRIDE since the terrain engine sets back face culling.)
-        ss->setAttributeAndModes( new osg::CullFace(), osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
+        ss->setAttributeAndModes( 
+            new osg::CullFace(), 
+            osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
 
         apply( _options );
     }
@@ -143,6 +171,8 @@ OceanSurfaceContainer::apply( const OceanSurfaceOptions& options )
     _lowFeather->set( *options.lowFeatherOffset() );
     _highFeather->set( *options.highFeatherOffset() );
     _baseColor->set( *options.baseColor() );
+    _maxRange->set( *options.maxRange() );
+    _fadeRange->set( *options.fadeRange() );
 }
 
 
diff --git a/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp b/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp
index 37d7414..fab5d51 100644
--- a/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp
+++ b/src/osgEarthDrivers/ocean_surface/ReaderWriterOceanSurface.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/osg/OSGOptions b/src/osgEarthDrivers/osg/OSGOptions
index c3966a6..4cc19a1 100644
--- a/src/osgEarthDrivers/osg/OSGOptions
+++ b/src/osgEarthDrivers/osg/OSGOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/osg/OSGTileSource.cpp b/src/osgEarthDrivers/osg/OSGTileSource.cpp
index b4a0846..1cd3715 100644
--- a/src/osgEarthDrivers/osg/OSGTileSource.cpp
+++ b/src/osgEarthDrivers/osg/OSGTileSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/refresh/ReaderWriterRefresh.cpp b/src/osgEarthDrivers/refresh/ReaderWriterRefresh.cpp
index 40e6444..53b8990 100644
--- a/src/osgEarthDrivers/refresh/ReaderWriterRefresh.cpp
+++ b/src/osgEarthDrivers/refresh/ReaderWriterRefresh.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/refresh/RefreshOptions b/src/osgEarthDrivers/refresh/RefreshOptions
index 8265ade..e2ef4eb 100644
--- a/src/osgEarthDrivers/refresh/RefreshOptions
+++ b/src/osgEarthDrivers/refresh/RefreshOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/script_engine_v8/JSWrappers b/src/osgEarthDrivers/script_engine_v8/JSWrappers
index a1f4318..cee4554 100644
--- a/src/osgEarthDrivers/script_engine_v8/JSWrappers
+++ b/src/osgEarthDrivers/script_engine_v8/JSWrappers
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/script_engine_v8/JSWrappers.cpp b/src/osgEarthDrivers/script_engine_v8/JSWrappers.cpp
index e4000fa..1858e98 100644
--- a/src/osgEarthDrivers/script_engine_v8/JSWrappers.cpp
+++ b/src/osgEarthDrivers/script_engine_v8/JSWrappers.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8 b/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8
index 86da96e..efdaff2 100644
--- a/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8
+++ b/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8.cpp b/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8.cpp
index ac6fd07..dba0abe 100644
--- a/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8.cpp
+++ b/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8Factory.cpp b/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8Factory.cpp
index 4d38e95..3088009 100644
--- a/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8Factory.cpp
+++ b/src/osgEarthDrivers/script_engine_v8/JavascriptEngineV8Factory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/script_engine_v8/V8Util b/src/osgEarthDrivers/script_engine_v8/V8Util
index cbf19ec..fbf388a 100644
--- a/src/osgEarthDrivers/script_engine_v8/V8Util
+++ b/src/osgEarthDrivers/script_engine_v8/V8Util
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp b/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp
index dbc93fc..6c4208a 100644
--- a/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp
+++ b/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/tilecache/TileCacheOptions b/src/osgEarthDrivers/tilecache/TileCacheOptions
index 123d34b..27c89e9 100644
--- a/src/osgEarthDrivers/tilecache/TileCacheOptions
+++ b/src/osgEarthDrivers/tilecache/TileCacheOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp b/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp
index 4bff990..033be09 100644
--- a/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp
+++ b/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/tileservice/TileServiceOptions b/src/osgEarthDrivers/tileservice/TileServiceOptions
index 208ff74..dee354d 100644
--- a/src/osgEarthDrivers/tileservice/TileServiceOptions
+++ b/src/osgEarthDrivers/tileservice/TileServiceOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp b/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp
index 4681424..5a743a6 100644
--- a/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp
+++ b/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -129,7 +129,7 @@ public:
     osg::Image* createImage(const TileKey&        key,
                             ProgressCallback*     progress )
     {
-        if (_tileMap.valid() && key.getLevelOfDetail() <= getMaxDataLevel() )
+        if (_tileMap.valid() && key.getLevelOfDetail() <= _tileMap->getMaxLevel() )
         {
             std::string image_url = _tileMap->getURL( key, _invertY );
                 
diff --git a/src/osgEarthDrivers/tms/TMSOptions b/src/osgEarthDrivers/tms/TMSOptions
index 32a1e01..630a9f9 100644
--- a/src/osgEarthDrivers/tms/TMSOptions
+++ b/src/osgEarthDrivers/tms/TMSOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vdatum_egm2008/EGM2008.cpp b/src/osgEarthDrivers/vdatum_egm2008/EGM2008.cpp
index c1eb881..cd7c4bb 100644
--- a/src/osgEarthDrivers/vdatum_egm2008/EGM2008.cpp
+++ b/src/osgEarthDrivers/vdatum_egm2008/EGM2008.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vdatum_egm2008/EGM2008Grid.h b/src/osgEarthDrivers/vdatum_egm2008/EGM2008Grid.h
index 8a2d6c8..e17f076 100644
--- a/src/osgEarthDrivers/vdatum_egm2008/EGM2008Grid.h
+++ b/src/osgEarthDrivers/vdatum_egm2008/EGM2008Grid.h
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vdatum_egm84/EGM84.cpp b/src/osgEarthDrivers/vdatum_egm84/EGM84.cpp
index 5b6aafe..d48ef1d 100644
--- a/src/osgEarthDrivers/vdatum_egm84/EGM84.cpp
+++ b/src/osgEarthDrivers/vdatum_egm84/EGM84.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vdatum_egm84/EGM84Grid.h b/src/osgEarthDrivers/vdatum_egm84/EGM84Grid.h
index 8856b6e..f1da3f8 100644
--- a/src/osgEarthDrivers/vdatum_egm84/EGM84Grid.h
+++ b/src/osgEarthDrivers/vdatum_egm84/EGM84Grid.h
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vdatum_egm96/EGM96.cpp b/src/osgEarthDrivers/vdatum_egm96/EGM96.cpp
index b5489a7..eda6b8b 100644
--- a/src/osgEarthDrivers/vdatum_egm96/EGM96.cpp
+++ b/src/osgEarthDrivers/vdatum_egm96/EGM96.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vdatum_egm96/EGM96Grid.h b/src/osgEarthDrivers/vdatum_egm96/EGM96Grid.h
index ddaa973..4794ff9 100644
--- a/src/osgEarthDrivers/vdatum_egm96/EGM96Grid.h
+++ b/src/osgEarthDrivers/vdatum_egm96/EGM96Grid.h
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp b/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp
index e194d79..3b44800 100644
--- a/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp
+++ b/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/vpb/VPBOptions b/src/osgEarthDrivers/vpb/VPBOptions
index 6e418b3..4aa8529 100644
--- a/src/osgEarthDrivers/vpb/VPBOptions
+++ b/src/osgEarthDrivers/vpb/VPBOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/wcs/ReaderWriterWCS.cpp b/src/osgEarthDrivers/wcs/ReaderWriterWCS.cpp
index 565bb06..41aaf3a 100644
--- a/src/osgEarthDrivers/wcs/ReaderWriterWCS.cpp
+++ b/src/osgEarthDrivers/wcs/ReaderWriterWCS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/wcs/WCS11Source.cpp b/src/osgEarthDrivers/wcs/WCS11Source.cpp
index cb0b153..3dc3814 100644
--- a/src/osgEarthDrivers/wcs/WCS11Source.cpp
+++ b/src/osgEarthDrivers/wcs/WCS11Source.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -42,18 +42,15 @@ _options  ( options )
 }
 
 
-void
-WCS11Source::initialize(const osgDB::Options* dbOptions,
-                        const Profile*        overrideProfile )
-{
-    if ( !getProfile() )
-    {
-        //TODO: fetch GetCapabilities and set profile from there.
-        setProfile( osgEarth::Registry::instance()->getGlobalGeodeticProfile() );
-    }
 
+osgEarth::TileSource::Status WCS11Source::initialize(const osgDB::Options* dbOptions)
+{        
+    //TODO: fetch GetCapabilities and set profile from there.
+    setProfile( osgEarth::Registry::instance()->getGlobalGeodeticProfile() );
     _dbOptions = Registry::instance()->cloneOrCreateOptions( dbOptions );
     CachePolicy::NO_CACHE.apply( _dbOptions.get() );
+
+    return STATUS_OK;
 }
 
 
diff --git a/src/osgEarthDrivers/wcs/WCS11Source.h b/src/osgEarthDrivers/wcs/WCS11Source.h
index 5c5051f..4ca8a73 100644
--- a/src/osgEarthDrivers/wcs/WCS11Source.h
+++ b/src/osgEarthDrivers/wcs/WCS11Source.h
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -39,9 +39,7 @@ public:
 
 public: // TileSource interface
 
-    void initialize(
-        const osgDB::Options* dbOptions,
-        const Profile*        overrideProfile );
+    Status initialize(const osgDB::Options* dbOptions);
     
     osg::Image* createImage( 
         const TileKey&        key,
diff --git a/src/osgEarthDrivers/wcs/WCSOptions b/src/osgEarthDrivers/wcs/WCSOptions
index 57d2d4f..ce1a14e 100644
--- a/src/osgEarthDrivers/wcs/WCSOptions
+++ b/src/osgEarthDrivers/wcs/WCSOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp b/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp
index 2ba5988..c8c7fc5 100644
--- a/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp
+++ b/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
 #include <osgEarth/TileSource>
 #include <osgEarth/ImageToHeightFieldConverter>
 #include <osgEarth/Registry>
+#include <osgEarth/TimeControl>
 #include <osgEarth/XmlUtils>
 #include <osgEarthUtil/WMS>
 #include <osgDB/FileNameUtils>
@@ -43,30 +44,43 @@ using namespace osgEarth;
 using namespace osgEarth::Util;
 using namespace osgEarth::Drivers;
 
-// All looping ImageSequences deriving from this class will by in sync due to
-// a shared reference time.
-class SyncImageSequence : public osg::ImageSequence {
-public:
-    SyncImageSequence()
-    { 
-    }
+//----------------------------------------------------------------------------
 
-    virtual void update(osg::NodeVisitor* nv) {
-        setReferenceTime( 0.0 );
-        osg::ImageSequence::update( nv );
-    }
-};
+namespace
+{
+    // All looping ImageSequences deriving from this class will be in sync due to
+    // a shared reference time.
+    struct SyncImageSequence : public osg::ImageSequence
+    {
+        SyncImageSequence() : osg::ImageSequence() { }
 
+        virtual void update(osg::NodeVisitor* nv)
+        {
+            setReferenceTime( 0.0 );
+            osg::ImageSequence::update( nv );
+        }
+    };
+}
 
-class WMSSource : public TileSource
+//----------------------------------------------------------------------------
+
+class WMSSource : public TileSource, public SequenceControl
 {
 public:
 	WMSSource( const TileSourceOptions& options ) : TileSource( options ), _options(options)
     {
+        _isPlaying     = false;
+
         if ( _options.times().isSet() )
         {
             StringTokenizer( *_options.times(), _timesVec, ",", "", false, true );
             OE_INFO << LC << "WMS-T: found " << _timesVec.size() << " times." << std::endl;
+
+            for( unsigned i=0; i<_timesVec.size(); ++i )
+            {
+                _seqFrameInfoVec.push_back(SequenceFrameInfo());
+                _seqFrameInfoVec.back().timeIdentifier = _timesVec[i];
+            }
         }
 
         // localize it since we might override them:
@@ -127,7 +141,8 @@ public:
 
         // first the mandatory keys:
         buf
-            << std::fixed << _options.url()->full() << sep            
+            << std::fixed << _options.url()->full() << sep
+	    << "SERVICE=WMS"
             << "&VERSION=" << _options.wmsVersion().value()
             << "&REQUEST=GetMap"
             << "&LAYERS=" << _options.layers().value()
@@ -424,10 +439,11 @@ public:
     osg::Image* createImageSequence( const TileKey& key, ProgressCallback* progress )
     {
         osg::ImageSequence* seq = new SyncImageSequence();
-
+        
         seq->setLoopingMode( osg::ImageStream::LOOPING );
         seq->setLength( _options.secondsPerFrame().value() * (double)_timesVec.size() );
-        seq->play();
+        if ( this->isSequencePlaying() )
+            seq->play();
 
         for( unsigned int r=0; r<_timesVec.size(); ++r )
         {
@@ -450,6 +466,7 @@ public:
             }
         }
 
+        _sequenceCache.insert( seq );
         return seq;
     }
 
@@ -503,15 +520,75 @@ public:
         return _formatToUse;
     }
 
+
+public: // SequenceControl
+
+    /** Whether the implementation supports these methods */
+    bool supportsSequenceControl() const
+    {
+        return _timesVec.size() > 1;
+    }
+
+    /** Starts playback */
+    void playSequence()
+    {
+        //todo
+        _isPlaying = true;
+    }
+
+    /** Stops playback */
+    void pauseSequence()
+    {
+        //todo
+        _isPlaying = false;
+    }
+
+    /** Seek to a specific frame */
+    void seekToSequenceFrame(unsigned frame)
+    {
+        //todo
+    }
+
+    /** Whether the object is in playback mode */
+    bool isSequencePlaying() const
+    {
+        return _isPlaying;
+    }
+
+    /** Gets data about the current frame in the sequence */
+    const std::vector<SequenceFrameInfo>& getSequenceFrameInfo() const
+    {
+        return _seqFrameInfoVec;
+    }
+
+    /** Index of current frame */
+    int getCurrentSequenceFrameIndex( const osg::FrameStamp* fs ) const
+    {
+        if ( _seqFrameInfoVec.size() == 0 )
+            return 0;
+
+        double len = _options.secondsPerFrame().value() * (double)_timesVec.size();
+        double t   = fmod( fs->getSimulationTime(), len ) / len;
+        return osg::clampBetween(
+            (int)(t * (double)_seqFrameInfoVec.size()), 
+            (int)0, 
+            (int)_seqFrameInfoVec.size()-1);
+    }
+
+
 private:
-    const WMSOptions _options;
-    std::string _formatToUse;
-    std::string _srsToUse;
-    osg::ref_ptr<TileService> _tileService;
-    osg::ref_ptr<const Profile> _profile;
-    std::string _prototype;
-    std::vector<std::string> _timesVec;
-    osg::ref_ptr<osgDB::Options> _dbOptions;
+    const WMSOptions                 _options;
+    std::string                      _formatToUse;
+    std::string                      _srsToUse;
+    osg::ref_ptr<TileService>        _tileService;
+    osg::ref_ptr<const Profile>      _profile;
+    std::string                      _prototype;
+    std::vector<std::string>         _timesVec;
+    osg::ref_ptr<osgDB::Options>     _dbOptions;
+    bool                             _isPlaying;
+    std::vector<SequenceFrameInfo>   _seqFrameInfoVec;
+
+    mutable Threading::ThreadSafeObserverSet<osg::ImageSequence> _sequenceCache;
 };
 
 
diff --git a/src/osgEarthDrivers/wms/TileService b/src/osgEarthDrivers/wms/TileService
index a3b85e5..e5c7ab5 100644
--- a/src/osgEarthDrivers/wms/TileService
+++ b/src/osgEarthDrivers/wms/TileService
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/wms/TileService.cpp b/src/osgEarthDrivers/wms/TileService.cpp
index fde839d..83cf17b 100644
--- a/src/osgEarthDrivers/wms/TileService.cpp
+++ b/src/osgEarthDrivers/wms/TileService.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/wms/WMSOptions b/src/osgEarthDrivers/wms/WMSOptions
index f1c8e35..f1d09b4 100644
--- a/src/osgEarthDrivers/wms/WMSOptions
+++ b/src/osgEarthDrivers/wms/WMSOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/xyz/ReaderWriterXYZ.cpp b/src/osgEarthDrivers/xyz/ReaderWriterXYZ.cpp
index 3a69175..70dd78a 100644
--- a/src/osgEarthDrivers/xyz/ReaderWriterXYZ.cpp
+++ b/src/osgEarthDrivers/xyz/ReaderWriterXYZ.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/xyz/XYZOptions b/src/osgEarthDrivers/xyz/XYZOptions
index 41f60f5..2c95c35 100644
--- a/src/osgEarthDrivers/xyz/XYZOptions
+++ b/src/osgEarthDrivers/xyz/XYZOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp b/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp
index f695813..5a36498 100644
--- a/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp
+++ b/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthDrivers/yahoo/YahooOptions b/src/osgEarthDrivers/yahoo/YahooOptions
index 92b99ac..37d31fc 100644
--- a/src/osgEarthDrivers/yahoo/YahooOptions
+++ b/src/osgEarthDrivers/yahoo/YahooOptions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/AltitudeFilter b/src/osgEarthFeatures/AltitudeFilter
index 7bf2935..a5d913e 100644
--- a/src/osgEarthFeatures/AltitudeFilter
+++ b/src/osgEarthFeatures/AltitudeFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/AltitudeFilter.cpp b/src/osgEarthFeatures/AltitudeFilter.cpp
index 5ee365d..d54657d 100644
--- a/src/osgEarthFeatures/AltitudeFilter.cpp
+++ b/src/osgEarthFeatures/AltitudeFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -47,13 +47,15 @@ AltitudeFilter::setPropertiesFromStyle( const Style& style )
 FilterContext
 AltitudeFilter::push( FeatureList& features, FilterContext& cx )
 {
-    bool clamp = 
-        _altitude.valid() && 
-        _altitude->clamping() != AltitudeSymbol::CLAMP_NONE &&
-        cx.getSession()       != 0L &&
-        cx.profile()          != 0L;
-
-    if ( clamp )
+    bool clampToMap = 
+        _altitude.valid()                                          && 
+        _altitude->clamping()  != AltitudeSymbol::CLAMP_NONE       &&
+        _altitude->technique() == AltitudeSymbol::TECHNIQUE_MAP    &&
+        //_altitude->technique() != AltitudeSymbol::TECHNIQUE_SCENE  &&
+        cx.getSession()        != 0L                               &&
+        cx.profile()           != 0L;
+
+    if ( clampToMap )
         pushAndClamp( features, cx );
     else
         pushAndDontClamp( features, cx );
@@ -81,11 +83,11 @@ AltitudeFilter::pushAndDontClamp( FeatureList& features, FilterContext& cx )
 
         double scaleZ = 1.0;
         if ( _altitude.valid() && _altitude->verticalScale().isSet() )
-            scaleZ = feature->eval( scaleExpr );
+            scaleZ = feature->eval( scaleExpr, &cx );
 
         double offsetZ = 0.0;
         if ( _altitude.valid() && _altitude->verticalOffset().isSet() )
-            offsetZ = feature->eval( offsetExpr );
+            offsetZ = feature->eval( offsetExpr, &cx );
         
         GeometryIterator gi( feature->getGeometry() );
         while( gi.hasMore() )
@@ -139,6 +141,10 @@ AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx )
         _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ||
         _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE;
 
+    // whether to clamp every vertex (or just the centroid)
+    bool perVertex =
+        _altitude->binding() == AltitudeSymbol::BINDING_VERTEX;
+
     // whether the SRS's have a compatible vertical datum.
     bool vertEquiv =
         featureSRS->isVertEquivalentTo( mapSRS );
@@ -146,8 +152,6 @@ AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx )
     for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
     {
         Feature* feature = i->get();
-        //double maxGeomZ     = -DBL_MAX;
-        //double minGeomZ     =  DBL_MAX;
         double maxTerrainZ  = -DBL_MAX;
         double minTerrainZ  =  DBL_MAX;
         double minHAT       =  DBL_MAX;
@@ -170,34 +174,78 @@ AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx )
             // remains unchanged.
             if ( _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE )
             {
-                std::vector<double> elevations;
-                elevations.reserve( geom->size() );
-
-                if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) )
+                if ( perVertex )
                 {
-                    for( unsigned i=0; i<geom->size(); ++i )
-                    {
-                        osg::Vec3d& p = (*geom)[i];
-                        double z = p.z();
+                    std::vector<double> elevations;
+                    elevations.reserve( geom->size() );
 
-                        if ( !vertEquiv )
+                    if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) )
+                    {
+                        for( unsigned i=0; i<geom->size(); ++i )
                         {
-                            osg::Vec3d tempgeo;
-                            if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) )
-                                z = tempgeo.z();
-                        }
+                            osg::Vec3d& p = (*geom)[i];
+
+                            p.z() *= scaleZ;
+                            p.z() += offsetZ;
+
+                            double z = p.z();
+
+                            if ( !vertEquiv )
+                            {
+                                osg::Vec3d tempgeo;
+                                if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) )
+                                    z = tempgeo.z();
+                            }
 
-                        double hat = z - elevations[i];
+                            double hat = z - elevations[i];
 
-                        if ( hat > maxHAT )
-                            maxHAT = hat;
-                        if ( hat < minHAT )
-                            minHAT = hat;
+                            if ( hat > maxHAT )
+                                maxHAT = hat;
+                            if ( hat < minHAT )
+                                minHAT = hat;
 
-                        if ( elevations[i] > maxTerrainZ )
-                            maxTerrainZ = elevations[i];
-                        if ( elevations[i] < minTerrainZ )
-                            minTerrainZ = elevations[i];
+                            if ( elevations[i] > maxTerrainZ )
+                                maxTerrainZ = elevations[i];
+                            if ( elevations[i] < minTerrainZ )
+                                minTerrainZ = elevations[i];
+                        }
+                    }
+                }
+                else // per centroid
+                {
+                    osgEarth::Bounds bounds = geom->getBounds();
+                    const osg::Vec2d& center = bounds.center2d();
+                    GeoPoint centroid(featureSRS, center.x(), center.y());
+                    double   centroidElevation;
+
+                    if ( eq.getElevation( centroid, centroidElevation, _maxRes ) )
+                    {
+                        for( unsigned i=0; i<geom->size(); ++i )
+                        {
+                            osg::Vec3d& p = (*geom)[i];
+                            p.z() *= scaleZ;
+                            p.z() += offsetZ;
+
+                            double z = p.z();
+                            if ( !vertEquiv )
+                            {
+                                osg::Vec3d tempgeo;
+                                if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) )
+                                    z = tempgeo.z();
+                            }
+
+                            double hat = z - centroidElevation;
+
+                            if ( hat > maxHAT )
+                                maxHAT = hat;
+                            if ( hat < minHAT )
+                                minHAT = hat;
+                        }
+
+                        if ( centroidElevation > maxTerrainZ )
+                            maxTerrainZ = centroidElevation;
+                        if ( centroidElevation < minTerrainZ )
+                            minTerrainZ = centroidElevation;
                     }
                 }
             }
@@ -209,56 +257,125 @@ AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx )
                 osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = !vertEquiv ?
                     SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()) : 0L;
 
-                std::vector<double> elevations;
-                elevations.reserve( geom->size() );
-
-                if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) )
+                if ( perVertex )
                 {
-                    for( unsigned i=0; i<geom->size(); ++i )
-                    {
-                        osg::Vec3d& p = (*geom)[i];
+                    std::vector<double> elevations;
+                    elevations.reserve( geom->size() );
 
-                        double hat = p.z();
-                        p.z() = elevations[i] + p.z();
-
-                        // if necessary, convert the Z value (which is now in the map's SRS) back to
-                        // the feature's SRS.
-                        if ( !vertEquiv )
+                    if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) )
+                    {
+                        for( unsigned i=0; i<geom->size(); ++i )
                         {
-                            featureSRSwithMapVertDatum->transform(p, featureSRS, p);
+                            osg::Vec3d& p = (*geom)[i];
+
+                            p.z() *= scaleZ;
+                            p.z() += offsetZ;
+
+                            double hat = p.z();
+                            p.z() = elevations[i] + p.z();
+
+                            // if necessary, convert the Z value (which is now in the map's SRS) back to
+                            // the feature's SRS.
+                            if ( !vertEquiv )
+                            {
+                                featureSRSwithMapVertDatum->transform(p, featureSRS, p);
+                            }
+
+                            if ( hat > maxHAT )
+                                maxHAT = hat;
+                            if ( hat < minHAT )
+                                minHAT = hat;
+
+                            if ( elevations[i] > maxTerrainZ )
+                                maxTerrainZ = elevations[i];
+                            if ( elevations[i] < minTerrainZ )
+                                minTerrainZ = elevations[i];
                         }
+                    }
+                }
+                else // per-centroid
+                {
+                    osgEarth::Bounds bounds = geom->getBounds();
+                    const osg::Vec2d& center = bounds.center2d();
+                    GeoPoint centroid(featureSRS, center.x(), center.y());
+                    double   centroidElevation;
 
-                        if ( hat > maxHAT )
-                            maxHAT = hat;
-                        if ( hat < minHAT )
-                            minHAT = hat;
+                    if ( eq.getElevation( centroid, centroidElevation, _maxRes ) )
+                    {
+                        for( unsigned i=0; i<geom->size(); ++i )
+                        {
+                            osg::Vec3d& p = (*geom)[i];
+                            p.z() *= scaleZ;
+                            p.z() += offsetZ;
+
+                            double hat = p.z();
+                            p.z() = centroidElevation + p.z();
+
+                            // if necessary, convert the Z value (which is now in the map's SRS) back to
+                            // the feature's SRS.
+                            if ( !vertEquiv )
+                            {
+                                featureSRSwithMapVertDatum->transform(p, featureSRS, p);
+                            }
+
+                            if ( hat > maxHAT )
+                                maxHAT = hat;
+                            if ( hat < minHAT )
+                                minHAT = hat;
+                        }
 
-                        if ( elevations[i] > maxTerrainZ )
-                            maxTerrainZ = elevations[i];
-                        if ( elevations[i] < minTerrainZ )
-                            minTerrainZ = elevations[i];
+                        if ( centroidElevation > maxTerrainZ )
+                            maxTerrainZ = centroidElevation;
+                        if ( centroidElevation < minTerrainZ )
+                            minTerrainZ = centroidElevation;
                     }
                 }
-
             }
 
             // Clamp - replace the geometry's Z with the terrain height.
             else // CLAMP_TO_TERRAIN
             {
-                eq.getElevations( geom->asVector(), featureSRS, true, _maxRes );
-                
-                // if necessary, transform the Z values (which are now in the map SRS) back
-                // into the feature's SRS.
-                if ( !vertEquiv )
+                if ( perVertex )
                 {
-                    osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = !vertEquiv ?
-                        SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()) : 0L;
+                    eq.getElevations( geom->asVector(), featureSRS, true, _maxRes );
+                    
+                    // if necessary, transform the Z values (which are now in the map SRS) back
+                    // into the feature's SRS.
+                    if ( !vertEquiv )
+                    {
+                        osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum =
+                            SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString());
 
-                    osg::Vec3d tempgeo;
-                    for( unsigned i=0; i<geom->size(); ++i )
+                        osg::Vec3d tempgeo;
+                        for( unsigned i=0; i<geom->size(); ++i )
+                        {
+                            osg::Vec3d& p = (*geom)[i];
+                            featureSRSwithMapVertDatum->transform(p, featureSRS, p);
+                        }
+                    }
+                }
+                else // per-centroid
+                {
+                    osgEarth::Bounds bounds = geom->getBounds();
+                    const osg::Vec2d& center = bounds.center2d();
+                    GeoPoint centroid(featureSRS, center.x(), center.y());
+                    double   centroidElevation;
+
+                    osg::ref_ptr<const SpatialReference> featureSRSWithMapVertDatum;
+                    if ( !vertEquiv )
+                        featureSRSWithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString());
+
+                    if ( eq.getElevation( centroid, centroidElevation, _maxRes ) )
                     {
-                        osg::Vec3d& p = (*geom)[i];
-                        featureSRSwithMapVertDatum->transform(p, featureSRS, p);
+                        for( unsigned i=0; i<geom->size(); ++i )
+                        {
+                            osg::Vec3d& p = (*geom)[i];
+                            p.z() = centroidElevation;
+                            if ( !vertEquiv )
+                            {
+                                featureSRSWithMapVertDatum->transform(p, featureSRS, p);
+                            }
+                        }
                     }
                 }
             }
diff --git a/src/osgEarthFeatures/BufferFilter b/src/osgEarthFeatures/BufferFilter
index a520fef..ad199f3 100644
--- a/src/osgEarthFeatures/BufferFilter
+++ b/src/osgEarthFeatures/BufferFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -83,8 +83,8 @@ namespace osgEarth { namespace Features
         virtual FilterContext push( FeatureList& input, FilterContext& context );
 
     protected:
-        optional<double> _distance;
-        int _numQuadSegs;
+        optional<double>     _distance;
+        int                  _numQuadSegs;
         Stroke::LineCapStyle _capStyle;
     };
 
diff --git a/src/osgEarthFeatures/BufferFilter.cpp b/src/osgEarthFeatures/BufferFilter.cpp
index fc9fac6..8feed33 100644
--- a/src/osgEarthFeatures/BufferFilter.cpp
+++ b/src/osgEarthFeatures/BufferFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -45,7 +45,7 @@ OSGEARTH_REGISTER_SIMPLE_FEATUREFILTER(buffer, BufferFilter );
 BufferFilter::BufferFilter() :
 _distance   ( 1.0 ),
 _numQuadSegs( 0 ),
-_capStyle   ( Stroke::LINECAP_DEFAULT )
+_capStyle   ( Stroke::LINECAP_SQUARE )
 {
     //NOP
 }
@@ -61,7 +61,7 @@ _capStyle   ( rhs._capStyle )
 BufferFilter::BufferFilter( const Config& conf ) :
 _distance   ( 1.0 ),
 _numQuadSegs( 0 ),
-_capStyle   ( Stroke::LINECAP_DEFAULT )
+_capStyle   ( Stroke::LINECAP_SQUARE )
 {
     if (conf.key() == "buffer")
     {
@@ -99,8 +99,8 @@ BufferFilter::push( FeatureList& input, FilterContext& context )
         params._capStyle =
                 _capStyle == Stroke::LINECAP_ROUND  ? Symbology::BufferParameters::CAP_ROUND :
                 _capStyle == Stroke::LINECAP_SQUARE ? Symbology::BufferParameters::CAP_SQUARE :
-                _capStyle == Stroke::LINECAP_BUTT   ? Symbology::BufferParameters::CAP_FLAT :
-                Symbology::BufferParameters::CAP_SQUARE;
+                _capStyle == Stroke::LINECAP_FLAT   ? Symbology::BufferParameters::CAP_FLAT :
+                                                      Symbology::BufferParameters::CAP_SQUARE;
 
         params._cornerSegs = _numQuadSegs;
 
@@ -112,7 +112,7 @@ BufferFilter::push( FeatureList& input, FilterContext& context )
         else
         {
             i = input.erase( i );
-            OE_INFO << LC << "feature " << feature->getFID() << " yielded no geometry" << std::endl;
+            OE_DEBUG << LC << "feature " << feature->getFID() << " yielded no geometry" << std::endl;
         }
     }
 
diff --git a/src/osgEarthFeatures/BuildGeometryFilter b/src/osgEarthFeatures/BuildGeometryFilter
index bd0bcd9..7c6a338 100644
--- a/src/osgEarthFeatures/BuildGeometryFilter
+++ b/src/osgEarthFeatures/BuildGeometryFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/BuildGeometryFilter.cpp b/src/osgEarthFeatures/BuildGeometryFilter.cpp
index 30df3f6..4f3575e 100644
--- a/src/osgEarthFeatures/BuildGeometryFilter.cpp
+++ b/src/osgEarthFeatures/BuildGeometryFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -36,6 +36,7 @@
 #include <osgText/Text>
 #include <osgUtil/Tessellator>
 #include <osgUtil/Optimizer>
+#include <osgUtil/SmoothingVisitor>
 #include <osgDB/WriteFile>
 #include <osg/Version>
 
@@ -175,9 +176,9 @@ BuildGeometryFilter::process( FeatureList& features, const FilterContext& contex
 
             // resolve the color:
             osg::Vec4f primaryColor =
-                polySymbol ? polySymbol->fill()->color() :
-                lineSymbol ? lineSymbol->stroke()->color() :
-                pointSymbol ? pointSymbol->fill()->color() :
+                polySymbol ? osg::Vec4f(polySymbol->fill()->color()) :
+                lineSymbol ? osg::Vec4f(lineSymbol->stroke()->color()) :
+                pointSymbol ? osg::Vec4f(pointSymbol->fill()->color()) :
                 osg::Vec4f(1,1,1,1);
             
             osg::Geometry* osgGeom = new osg::Geometry();
@@ -264,7 +265,7 @@ BuildGeometryFilter::process( FeatureList& features, const FilterContext& contex
 
             // record the geometry's primitive set(s) in the index:
             if ( context.featureIndex() )
-                context.featureIndex()->tagPrimitiveSets( osgGeom, input->getFID() );
+                context.featureIndex()->tagPrimitiveSets( osgGeom, input );
 
             // build secondary geometry, if necessary (polygon outlines)
             if ( renderType == Geometry::TYPE_POLYGON && lineSymbol )
@@ -313,13 +314,17 @@ BuildGeometryFilter::process( FeatureList& features, const FilterContext& contex
 
                 applyLineAndPointSymbology( outline->getOrCreateStateSet(), lineSymbol, 0L );
 
+                // make normals before adding an outline
+                osgUtil::SmoothingVisitor sv;
+                _geode->accept( sv );
+
                 _geode->addDrawable( outline );
 
                 //_featureNode->addDrawable( outline, input->getFID() );
 
                 // Mark each primitive set with its feature ID.
                 if ( context.featureIndex() )
-                    context.featureIndex()->tagPrimitiveSets( outline, input->getFID() );
+                    context.featureIndex()->tagPrimitiveSets( outline, input );
             }
 
         }
@@ -374,6 +379,30 @@ BuildGeometryFilter::buildPolygon(Geometry*               ring,
         //tess.setBoundaryOnly( true );
         tess.retessellatePolygons( *osgGeom );
     }
+
+    //// Normal computation.
+    //// Not completely correct, but better than no normals at all. TODO: update this
+    //// to generate a proper normal vector in ECEF mode.
+    ////
+    //// We cannot accurately rely on triangles from the tessellation, since we could have
+    //// very "degraded" triangles (close to a line), and the normal computation would be bad.
+    //// In this case, we would have to average the normal vector over each triangle of the polygon.
+    //// The Newell's formula is simpler and more direct here.
+    //osg::Vec3 normal( 0.0, 0.0, 0.0 );
+    //for ( size_t i = 0; i < poly->size(); ++i )
+    //{
+    //    osg::Vec3 pi = (*poly)[i];
+    //    osg::Vec3 pj = (*poly)[ (i+1) % poly->size() ];
+    //    normal[0] += ( pi[1] - pj[1] ) * ( pi[2] + pj[2] );
+    //    normal[1] += ( pi[2] - pj[2] ) * ( pi[0] + pj[0] );
+    //    normal[2] += ( pi[0] - pj[0] ) * ( pi[1] + pj[1] );
+    //}
+    //normal.normalize();
+
+    //osg::Vec3Array* normals = new osg::Vec3Array();
+    //normals->push_back( normal );
+    //osgGeom->setNormalArray( normals );
+    //osgGeom->setNormalBinding( osg::Geometry::BIND_OVERALL );
 }
 
 
@@ -389,7 +418,16 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context )
     // convert all geom to triangles and consolidate into minimal set of Geometries
     if ( !_featureNameExpr.isSet() )
     {
+#if 1
         MeshConsolidator::run( *_geode.get() );
+#else
+        osgUtil::Optimizer opt;
+        opt.optimize( _geode.get(),
+            osgUtil::Optimizer::VERTEX_PRETRANSFORM |
+            osgUtil::Optimizer::INDEX_MESH |
+            osgUtil::Optimizer::VERTEX_POSTTRANSFORM |
+            osgUtil::Optimizer::MERGE_GEOMETRY );
+#endif
     }
 
     osg::Node* result = 0L;
diff --git a/src/osgEarthFeatures/BuildTextFilter b/src/osgEarthFeatures/BuildTextFilter
index 0eb94f5..c8380eb 100644
--- a/src/osgEarthFeatures/BuildTextFilter
+++ b/src/osgEarthFeatures/BuildTextFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/BuildTextFilter.cpp b/src/osgEarthFeatures/BuildTextFilter.cpp
index c135169..64d1392 100644
--- a/src/osgEarthFeatures/BuildTextFilter.cpp
+++ b/src/osgEarthFeatures/BuildTextFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/BuildTextOperator b/src/osgEarthFeatures/BuildTextOperator
index 269ece1..7d155b8 100644
--- a/src/osgEarthFeatures/BuildTextOperator
+++ b/src/osgEarthFeatures/BuildTextOperator
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/BuildTextOperator.cpp b/src/osgEarthFeatures/BuildTextOperator.cpp
index 66ef7eb..fc68683 100644
--- a/src/osgEarthFeatures/BuildTextOperator.cpp
+++ b/src/osgEarthFeatures/BuildTextOperator.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/CMakeLists.txt b/src/osgEarthFeatures/CMakeLists.txt
index 233dd2b..70caec3 100644
--- a/src/osgEarthFeatures/CMakeLists.txt
+++ b/src/osgEarthFeatures/CMakeLists.txt
@@ -26,7 +26,7 @@ SET(LIB_PUBLIC_HEADERS
     FeatureCursor
     FeatureDisplayLayout
     FeatureDrawSet
-	FeatureListSource
+    FeatureListSource
     FeatureModelGraph
     FeatureModelSource
     FeatureSource
@@ -35,11 +35,12 @@ SET(LIB_PUBLIC_HEADERS
     Filter
     FilterContext
     GeometryCompiler
-	GeometryUtils
+    GeometryUtils
     LabelSource
     MeshClamper
     OgrUtils
-    OptimizerHints	
+    OptimizerHints
+    PolygonizeLines
     ResampleFilter
     ScaleFilter
     Session
@@ -80,7 +81,9 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
 	GeometryUtils.cpp
     LabelSource.cpp
     MeshClamper.cpp
+    OgrUtils.cpp
     OptimizerHints.cpp
+    PolygonizeLines.cpp
     ResampleFilter.cpp
     ScaleFilter.cpp
     Session.cpp
diff --git a/src/osgEarthFeatures/CentroidFilter b/src/osgEarthFeatures/CentroidFilter
index 4cb414e..9911aff 100644
--- a/src/osgEarthFeatures/CentroidFilter
+++ b/src/osgEarthFeatures/CentroidFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/CentroidFilter.cpp b/src/osgEarthFeatures/CentroidFilter.cpp
index 0b89bd1..5ea9214 100644
--- a/src/osgEarthFeatures/CentroidFilter.cpp
+++ b/src/osgEarthFeatures/CentroidFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/Common b/src/osgEarthFeatures/Common
index f2c11f6..f7b75ad 100644
--- a/src/osgEarthFeatures/Common
+++ b/src/osgEarthFeatures/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ConvertTypeFilter b/src/osgEarthFeatures/ConvertTypeFilter
index 039e5fc..329dde5 100644
--- a/src/osgEarthFeatures/ConvertTypeFilter
+++ b/src/osgEarthFeatures/ConvertTypeFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ConvertTypeFilter.cpp b/src/osgEarthFeatures/ConvertTypeFilter.cpp
index a52b5f0..2c0147f 100644
--- a/src/osgEarthFeatures/ConvertTypeFilter.cpp
+++ b/src/osgEarthFeatures/ConvertTypeFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/CropFilter b/src/osgEarthFeatures/CropFilter
index 8aa4d8e..62e8029 100644
--- a/src/osgEarthFeatures/CropFilter
+++ b/src/osgEarthFeatures/CropFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/CropFilter.cpp b/src/osgEarthFeatures/CropFilter.cpp
index 00b0454..fe24d66 100644
--- a/src/osgEarthFeatures/CropFilter.cpp
+++ b/src/osgEarthFeatures/CropFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ExtrudeGeometryFilter b/src/osgEarthFeatures/ExtrudeGeometryFilter
index 8976ed0..bbe1d54 100644
--- a/src/osgEarthFeatures/ExtrudeGeometryFilter
+++ b/src/osgEarthFeatures/ExtrudeGeometryFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -82,6 +82,12 @@ namespace osgEarth { namespace Features
         void setFeatureNameExpr( const StringExpression& expr ) { _featureNameExpr = expr; }
         const StringExpression& getFeatureNameExpr() const { return _featureNameExpr; }
 
+        /**
+         * Whether or not to use vertex buffer objects
+         */
+        optional<bool>& useVertexBufferObjects() { return _useVertexBufferObjects;}
+        const optional<bool>& useVertexBufferObjects() const { return _useVertexBufferObjects;}
+
 
     protected:
 
@@ -100,6 +106,7 @@ namespace osgEarth { namespace Features
         optional<NumericExpression>    _heightOffsetExpr;
         optional<NumericExpression>    _heightExpr;
         bool                           _makeStencilVolume;
+        optional<bool>                 _useVertexBufferObjects;
 
         Style                          _style;
         bool                           _styleDirty;
@@ -119,7 +126,7 @@ namespace osgEarth { namespace Features
             osg::Drawable*      drawable, 
             osg::StateSet*      stateSet, 
             const std::string&  name,
-            FeatureID           fid,
+            Feature*            feature,
             FeatureSourceIndex* index);
         
         bool process( 
diff --git a/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp b/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp
index cac28c5..0e00083 100644
--- a/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp
+++ b/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -38,8 +38,6 @@
 
 #define LC "[ExtrudeGeometryFilter] "
 
-#define USE_VBOS true
-
 using namespace osgEarth;
 using namespace osgEarth::Features;
 using namespace osgEarth::Symbology;
@@ -80,7 +78,8 @@ _maxAngle_deg       ( 5.0 ),
 _mergeGeometry      ( true ),
 _wallAngleThresh_deg( 60.0 ),
 _styleDirty         ( true ),
-_makeStencilVolume  ( false )
+_makeStencilVolume  ( false ),
+_useVertexBufferObjects( true )
 {
     //NOP
 }
@@ -633,7 +632,7 @@ void
 ExtrudeGeometryFilter::addDrawable(osg::Drawable*      drawable,
                                    osg::StateSet*      stateSet,
                                    const std::string&  name,
-                                   FeatureID           fid,
+                                   Feature*            feature,
                                    FeatureSourceIndex* index )
 {
     // find the geode for the active stateset, creating a new one if necessary. NULL is a 
@@ -655,7 +654,7 @@ ExtrudeGeometryFilter::addDrawable(osg::Drawable*      drawable,
 
     if ( index )
     {
-        index->tagPrimitiveSets( drawable, fid );
+        index->tagPrimitiveSets( drawable, feature );
     }
 }
 
@@ -676,7 +675,7 @@ ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
             Geometry* part = iter.next();
 
             osg::ref_ptr<osg::Geometry> walls = new osg::Geometry();
-            walls->setUseVertexBufferObjects(USE_VBOS);
+            walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
             
             osg::ref_ptr<osg::Geometry> rooflines = 0L;
             osg::ref_ptr<osg::Geometry> baselines = 0L;
@@ -685,7 +684,7 @@ ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
             if ( part->getType() == Geometry::TYPE_POLYGON )
             {
                 rooflines = new osg::Geometry();
-                rooflines->setUseVertexBufferObjects(USE_VBOS);
+                rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
 
                 // prep the shapes by making sure all polys are open:
                 static_cast<Polygon*>(part)->open();
@@ -695,14 +694,14 @@ ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
             if ( _outlineSymbol != 0L )
             {
                 outlines = new osg::Geometry();
-                outlines->setUseVertexBufferObjects(USE_VBOS);
+                outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
             }
 
             // make a base cap if we're doing stencil volumes.
             if ( _makeStencilVolume )
             {
                 baselines = new osg::Geometry();
-                baselines->setUseVertexBufferObjects(USE_VBOS);
+                baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
             }
 
             // calculate the extrusion height:
@@ -831,7 +830,7 @@ ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
 
                     // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it.
                     // TODO: why??
-                    rooflines->setDataVariance( osg::Object::DYNAMIC );
+                    //rooflines->setDataVariance( osg::Object::DYNAMIC );
 
                     if ( roofSkin )
                     {
@@ -852,23 +851,22 @@ ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
                     name = input->eval( _featureNameExpr, &context );
 
                 FeatureSourceIndex* index = context.featureIndex();
-                FeatureID fid = input->getFID();
 
-                addDrawable( walls.get(), wallStateSet, name, fid, index );
+                addDrawable( walls.get(), wallStateSet, name, input, index );
 
                 if ( rooflines.valid() )
                 {
-                    addDrawable( rooflines.get(), roofStateSet, name, fid, index );
+                    addDrawable( rooflines.get(), roofStateSet, name, input, index );
                 }
 
                 if ( baselines.valid() )
                 {
-                    addDrawable( baselines.get(), 0L, name, fid, index );
+                    addDrawable( baselines.get(), 0L, name, input, index );
                 }
 
                 if ( outlines.valid() )
                 {
-                    addDrawable( outlines.get(), 0L, name, fid, index );
+                    addDrawable( outlines.get(), 0L, name, input, index );
                 }
             }   
         }
@@ -932,7 +930,16 @@ ExtrudeGeometryFilter::push( FeatureList& input, FilterContext& context )
     {
         for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i )
         {
+#if 1
             MeshConsolidator::run( *i->second.get() );
+#else
+        osgUtil::Optimizer opt;
+        opt.optimize( i->second.get(),
+            osgUtil::Optimizer::VERTEX_PRETRANSFORM |
+            osgUtil::Optimizer::INDEX_MESH |
+            osgUtil::Optimizer::VERTEX_POSTTRANSFORM |
+            osgUtil::Optimizer::MERGE_GEOMETRY );
+#endif
         }
     }
 
diff --git a/src/osgEarthFeatures/Feature b/src/osgEarthFeatures/Feature
index 40bf095..e8972ef 100644
--- a/src/osgEarthFeatures/Feature
+++ b/src/osgEarthFeatures/Feature
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
 #include <osgEarthFeatures/FilterContext>
 #include <osgEarthSymbology/Geometry>
 #include <osgEarthSymbology/Style>
+#include <osgEarth/GeoCommon>
 #include <osgEarth/SpatialReference>
 #include <osg/Array>
 #include <osg/Shape>
@@ -79,6 +80,9 @@ namespace osgEarth { namespace Features
         double      doubleValue;
         int         intValue;
         bool        boolValue;
+
+        //Whether the value is set.  A value of false means the value is effectively NULL
+        bool        set;
     };
 
     enum AttributeType
@@ -91,11 +95,11 @@ namespace osgEarth { namespace Features
     };
 
     struct OSGEARTHFEATURES_EXPORT AttributeValue : public std::pair<AttributeType,AttributeValueUnion>
-    {
+    {    
         std::string getString() const;
         double getDouble( double defaultValue =0.0 ) const;
         int getInt( int defaultValue =0 ) const;
-        bool getBool( bool defaultValue =false ) const;
+        bool getBool( bool defaultValue =false ) const;              
     };
 
     typedef std::map<std::string, AttributeValue> AttributeTable;
@@ -179,6 +183,10 @@ namespace osgEarth { namespace Features
         void set( const std::string& name, int value );
         void set( const std::string& name, bool value );
 
+        /** Sets the attribute to NULL */         
+        void setNull( const std::string& name );
+        void setNull( const std::string& name, AttributeType type );
+
         bool hasAttr( const std::string& name ) const;
 
         std::string getString( const std::string& name ) const;
@@ -186,6 +194,11 @@ namespace osgEarth { namespace Features
         int getInt( const std::string& name, int defaultValue =0 ) const;
         bool getBool( const std::string& name, bool defaultValue =false ) const;
 
+        /**
+         * Gets whether the attribute is set, meaning it is non-NULL
+         */
+        bool isSet( const std::string& name ) const;
+
         /** Embedded style. */
         optional<Style>& style() { return _style; }
         const optional<Style>& style() const { return _style; }
diff --git a/src/osgEarthFeatures/Feature.cpp b/src/osgEarthFeatures/Feature.cpp
index 7541182..5a590df 100644
--- a/src/osgEarthFeatures/Feature.cpp
+++ b/src/osgEarthFeatures/Feature.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -209,6 +209,7 @@ Feature::set( const std::string& name, const std::string& value )
     AttributeValue& a = _attrs[name];
     a.first = ATTRTYPE_STRING;
     a.second.stringValue = value;
+    a.second.set = true;
 }
 
 void
@@ -217,6 +218,7 @@ Feature::set( const std::string& name, double value )
     AttributeValue& a = _attrs[name];
     a.first = ATTRTYPE_DOUBLE;
     a.second.doubleValue = value;
+    a.second.set = true;
 }
 
 void
@@ -225,6 +227,7 @@ Feature::set( const std::string& name, int value )
     AttributeValue& a = _attrs[name];
     a.first = ATTRTYPE_INT;
     a.second.intValue = value;
+    a.second.set = true;
 }
 
 void
@@ -233,8 +236,27 @@ Feature::set( const std::string& name, bool value )
     AttributeValue& a = _attrs[name];
     a.first = ATTRTYPE_BOOL;
     a.second.boolValue = value;
+    a.second.set = true;
 }
 
+void
+Feature::setNull( const std::string& name)
+{
+    AttributeValue& a = _attrs[name];    
+    a.second.set = false;
+}
+
+void
+Feature::setNull( const std::string& name, AttributeType type)
+{
+    AttributeValue& a = _attrs[name];
+    a.first = type;    
+    a.second.set = false;
+}
+
+
+
+
 bool
 Feature::hasAttr( const std::string& name ) const
 {
@@ -269,6 +291,13 @@ Feature::getBool( const std::string& name, bool defaultValue ) const
     return i != _attrs.end()? i->second.getBool(defaultValue) : defaultValue;
 }
 
+bool
+Feature::isSet( const std::string& name) const
+{
+    AttributeTable::const_iterator i = _attrs.find(toLower(name));
+    return i != _attrs.end()? i->second.second.set : false;
+}
+
 double
 Feature::eval( NumericExpression& expr, FilterContext const* context ) const
 {
@@ -327,8 +356,7 @@ Feature::eval( StringExpression& expr, FilterContext const* context ) const
         }
       }
 
-      if (!val.empty())
-        expr.set( *i, val );
+      expr.set( *i, val );
     }
 
     return expr.eval();
@@ -440,19 +468,47 @@ Feature::getGeoJSON()
         {
             if (itr->second.first == ATTRTYPE_INT)
             {
-                props[itr->first] = itr->second.getInt();
+                if (itr->second.second.set)
+                {
+                    props[itr->first] = itr->second.getInt();
+                }
+                else
+                {
+                    props[itr->first] = Json::nullValue;
+                }
             }
             else if (itr->second.first == ATTRTYPE_DOUBLE)
             {
-                props[itr->first] = itr->second.getDouble();
+                if (itr->second.second.set)
+                {
+                    props[itr->first] = itr->second.getDouble();
+                }
+                else
+                {
+                    props[itr->first] = Json::nullValue;
+                }
             }
             else if (itr->second.first == ATTRTYPE_BOOL)
             {
-                props[itr->first] = itr->second.getBool();
+                if (itr->second.second.set)
+                {
+                    props[itr->first] = itr->second.getBool();
+                }
+                else
+                {
+                    props[itr->first] = Json::nullValue;
+                }
             }
             else
             {
-                props[itr->first] = itr->second.getString();
+                if (itr->second.second.set)
+                {
+                    props[itr->first] = itr->second.getString();
+                }
+                else
+                {
+                    props[itr->first] = Json::nullValue;
+                }
             }            
         }
     } 
diff --git a/src/osgEarthFeatures/FeatureCursor b/src/osgEarthFeatures/FeatureCursor
index ecd2558..7fc0f8c 100644
--- a/src/osgEarthFeatures/FeatureCursor
+++ b/src/osgEarthFeatures/FeatureCursor
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureCursor.cpp b/src/osgEarthFeatures/FeatureCursor.cpp
index a093cc6..a677817 100644
--- a/src/osgEarthFeatures/FeatureCursor.cpp
+++ b/src/osgEarthFeatures/FeatureCursor.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureDisplayLayout b/src/osgEarthFeatures/FeatureDisplayLayout
index a4f1298..9c14060 100644
--- a/src/osgEarthFeatures/FeatureDisplayLayout
+++ b/src/osgEarthFeatures/FeatureDisplayLayout
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureDisplayLayout.cpp b/src/osgEarthFeatures/FeatureDisplayLayout.cpp
index 6737164..17c14db 100644
--- a/src/osgEarthFeatures/FeatureDisplayLayout.cpp
+++ b/src/osgEarthFeatures/FeatureDisplayLayout.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureDrawSet b/src/osgEarthFeatures/FeatureDrawSet
index 95ce676..61a7897 100644
--- a/src/osgEarthFeatures/FeatureDrawSet
+++ b/src/osgEarthFeatures/FeatureDrawSet
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureDrawSet.cpp b/src/osgEarthFeatures/FeatureDrawSet.cpp
index 9638279..340a248 100644
--- a/src/osgEarthFeatures/FeatureDrawSet.cpp
+++ b/src/osgEarthFeatures/FeatureDrawSet.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureListSource b/src/osgEarthFeatures/FeatureListSource
index ea441cc..c2e2464 100644
--- a/src/osgEarthFeatures/FeatureListSource
+++ b/src/osgEarthFeatures/FeatureListSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureListSource.cpp b/src/osgEarthFeatures/FeatureListSource.cpp
index ee25c98..50ebab1 100644
--- a/src/osgEarthFeatures/FeatureListSource.cpp
+++ b/src/osgEarthFeatures/FeatureListSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureModelGraph b/src/osgEarthFeatures/FeatureModelGraph
index 9a5882d..79088a6 100644
--- a/src/osgEarthFeatures/FeatureModelGraph
+++ b/src/osgEarthFeatures/FeatureModelGraph
@@ -1,6 +1,6 @@
 /* --*-c++-*-- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -24,11 +24,17 @@
 #include <osgEarthFeatures/FeatureModelSource>
 #include <osgEarthFeatures/Session>
 #include <osgEarthSymbology/Style>
+#include <osgEarth/OverlayNode>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/ThreadingUtils>
 #include <osg/Node>
 #include <set>
 
+namespace osgEarth {
+    class ClampableNode;
+    class DrapeableNode;
+}
+
 namespace osgEarth { namespace Features
 {
     using namespace osgEarth;
@@ -78,6 +84,11 @@ namespace osgEarth { namespace Features
          */
         Session* getSession() { return _session; }
 
+		/**
+         * UID given to this feature graph when registered with the pseudo-loader
+         */
+        UID getUID() const { return _uid; }
+
         /**
          * Mark the feature graph dirty and in need of regeneration 
          */
@@ -140,6 +151,9 @@ namespace osgEarth { namespace Features
             const StringExpression& styleExpr,
             FeatureSourceIndex*     index,
             osg::Group*             parent);
+
+        osg::Group* getOrCreateStyleGroupFromFactory(
+            const Style& style);
        
         osg::BoundingSphered getBoundInWorldCoords( 
             const GeoExtent& extent, 
@@ -167,8 +181,25 @@ namespace osgEarth { namespace Features
         bool                             _dirty;
         bool                             _pendingUpdate;
         std::vector<const FeatureLevel*> _lodmap;
-        
+
+        osg::Group*                      _overlayInstalled;
+        osg::Group*                      _overlayPlaceholder;
+        ClampableNode*                   _clampable;
+        DrapeableNode*                   _drapeable;
+
+        enum OverlayChange {
+            OVERLAY_NO_CHANGE,
+            OVERLAY_INSTALL_PLACEHOLDER,
+            OVERLAY_INSTALL_CLAMPABLE,
+            OVERLAY_INSTALL_DRAPEABLE
+        };
+        OverlayChange                    _overlayChange;
+
         osg::ref_ptr<RefNodeOperationVector> _postMergeOperations;
+
+        void runPostMergeOperations(osg::Node* node);
+        void checkForGlobalAltitudeStyles(const Style& style);
+        void changeOverlay();
     };
 
 } } // namespace osgEarth::Features
diff --git a/src/osgEarthFeatures/FeatureModelGraph.cpp b/src/osgEarthFeatures/FeatureModelGraph.cpp
index 66b5b74..c1e0385 100644
--- a/src/osgEarthFeatures/FeatureModelGraph.cpp
+++ b/src/osgEarthFeatures/FeatureModelGraph.cpp
@@ -1,6 +1,6 @@
 /* --*-c++-*-- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -21,7 +21,9 @@
 #include <osgEarthFeatures/CropFilter>
 #include <osgEarthFeatures/FeatureSourceIndexNode>
 #include <osgEarth/Capabilities>
+#include <osgEarth/ClampableNode>
 #include <osgEarth/CullingUtils>
+#include <osgEarth/DrapeableNode>
 #include <osgEarth/ElevationLOD>
 #include <osgEarth/ElevationQuery>
 #include <osgEarth/FadeEffect>
@@ -44,9 +46,8 @@ using namespace osgEarth::Features;
 using namespace osgEarth::Symbology;
 
 #undef USE_PROXY_NODE_FOR_TESTING
-
-//#define OE_TEST OE_NULL
-#define OE_TEST OE_NOTICE
+#define OE_TEST OE_NULL
+//#define OE_TEST OE_NOTICE
 
 //---------------------------------------------------------------------------
 
@@ -83,15 +84,15 @@ namespace
         p->setFileName( 0, uri );
 #else
         PagedLODWithNodeOperations* p = new PagedLODWithNodeOperations(postMergeOps);
-        //osg::PagedLOD* p = new osg::PagedLOD();
         p->setCenter( bs.center() );
-        //p->setRadius( bs.radius() );
-        p->setRadius(std::max((float)bs.radius(),maxRange));
+        //p->setRadius(std::max((float)bs.radius(),maxRange));
+        p->setRadius( bs.radius() );
         p->setFileName( 0, uri );
         p->setRange( 0, minRange, maxRange );
         p->setPriorityOffset( 0, priOffset );
         p->setPriorityScale( 0, priScale );
 #endif
+
         return p;
     }
 }
@@ -157,6 +158,12 @@ struct osgEarthFeatureModelPseudoLoader : public osgDB::ReaderWriter
         FMGRegistry::const_iterator i = _fmgRegistry.find( uid );
         return i != _fmgRegistry.end() ? i->second.get() : 0L;
     }
+
+    /** User data structure for traversing the feature graph. */
+    struct CullUserData : public osg::Referenced
+    {
+        double _cameraElevation;
+    };
 };
 
 REGISTER_OSGPLUGIN(osgearth_pseudo_fmg, osgEarthFeatureModelPseudoLoader);
@@ -199,11 +206,16 @@ namespace
 FeatureModelGraph::FeatureModelGraph(Session*                         session,
                                      const FeatureModelSourceOptions& options,
                                      FeatureNodeFactory*              factory ) :
-_session      ( session ),
-_options      ( options ),
-_factory      ( factory ),
-_dirty        ( false ),
-_pendingUpdate( false )
+_session           ( session ),
+_options           ( options ),
+_factory           ( factory ),
+_dirty             ( false ),
+_pendingUpdate     ( false ),
+_overlayInstalled  ( 0L ),
+_overlayPlaceholder( 0L ),
+_clampable         ( 0L ),
+_drapeable         ( 0L ),
+_overlayChange     ( OVERLAY_NO_CHANGE )
 {
     _uid = osgEarthFeatureModelPseudoLoader::registerGraph( this );
 
@@ -293,7 +305,7 @@ _pendingUpdate( false )
 
     // If the user requests fade-in, install a post-merge operation that will set the 
     // proper fade time for paged nodes.
-    if ( _options.fadeInDuration().value() > 0.0f )
+    if ( _options.fading().isSet() )
     {
         addPostMergeOperation( new SetupFading() );
         OE_INFO << LC << "Added fading post-merge operation" << std::endl;
@@ -362,8 +374,11 @@ FeatureModelGraph::getBoundInWorldCoords(const GeoExtent& extent,
 
     if ( _session->getMapInfo().isGeocentric() )
     {
-        workingExtent.getSRS()->transformToECEF( center, center );
-        workingExtent.getSRS()->transformToECEF( corner, corner );
+        const SpatialReference* ecefSRS = workingExtent.getSRS()->getECEF();
+        workingExtent.getSRS()->transform( center, ecefSRS, center );
+        workingExtent.getSRS()->transform( corner, ecefSRS, corner );
+        //workingExtent.getSRS()->transformToECEF( center, center );
+        //workingExtent.getSRS()->transformToECEF( corner, corner );
     }
 
     return osg::BoundingSphered( center, (center-corner).length() );
@@ -377,9 +392,10 @@ FeatureModelGraph::setupPaging()
     osg::BoundingSphered bs = getBoundInWorldCoords( _usableMapExtent, &mapf );
 
     const FeatureProfile* featureProfile = _session->getFeatureSource()->getFeatureProfile();
-    if (featureProfile->getTiled() && 
-        !_options.layout()->tileSizeFactor().isSet() && 
-        (_options.layout()->maxRange().isSet() || _options.maxRange().isSet()))
+
+    optional<float> maxRangeOverride;
+
+    if (_options.layout()->maxRange().isSet() || _options.maxRange().isSet())
     {
         // select the max range either from the Layout or from the model layer options.
         float userMaxRange = FLT_MAX;
@@ -388,27 +404,39 @@ FeatureModelGraph::setupPaging()
         if ( _options.maxRange().isSet() )
             userMaxRange = std::min(userMaxRange, *_options.maxRange());
 
-        //Automatically compute the tileSizeFactor based on the max range
-        double width, height;
-        featureProfile->getProfile()->getTileDimensions(featureProfile->getFirstLevel(), width, height);
-
-        GeoExtent ext(featureProfile->getSRS(),
-                      featureProfile->getExtent().west(),
-                      featureProfile->getExtent().south(),
-                      featureProfile->getExtent().west() + width,
-                      featureProfile->getExtent().south() + height);
-        osg::BoundingSphered bounds = getBoundInWorldCoords( ext, &mapf);
-
-        float tileSizeFactor = userMaxRange / bounds.radius();
-        //The tilesize factor must be at least 1.0 to avoid culling the tile when you are within it's bounding sphere. 
-        tileSizeFactor = osg::maximum( tileSizeFactor, 1.0f);
-        OE_DEBUG << LC << "Computed a tilesize factor of " << tileSizeFactor << " with max range setting of " <<  userMaxRange << std::endl;
-        _options.layout()->tileSizeFactor() = tileSizeFactor * 1.5;
+        if (featureProfile->getTiled() )
+        {
+            if ( !_options.layout()->tileSizeFactor().isSet() )
+            {
+                //Automatically compute the tileSizeFactor based on the max range
+                double width, height;
+                featureProfile->getProfile()->getTileDimensions(featureProfile->getFirstLevel(), width, height);
+
+                GeoExtent ext(featureProfile->getSRS(),
+                              featureProfile->getExtent().west(),
+                              featureProfile->getExtent().south(),
+                              featureProfile->getExtent().west() + width,
+                              featureProfile->getExtent().south() + height);
+                osg::BoundingSphered bounds = getBoundInWorldCoords( ext, &mapf);
+
+                float tileSizeFactor = userMaxRange / bounds.radius();
+                //The tilesize factor must be at least 1.0 to avoid culling the tile when you are within it's bounding sphere. 
+                tileSizeFactor = osg::maximum( tileSizeFactor, 1.0f);
+                OE_DEBUG << LC << "Computed a tilesize factor of " << tileSizeFactor << " with max range setting of " <<  userMaxRange << std::endl;
+                _options.layout()->tileSizeFactor() = tileSizeFactor * 1.5;
+            }
+        }
+        else
+        {
+            // user set a max_range, but we'd not tiled. Just override the top level plod.
+            maxRangeOverride = userMaxRange;
+        }
     }
-   
 
     // calculate the max range for the top-level PLOD:
-    float maxRange = bs.radius() * _options.layout()->tileSizeFactor().value();
+    float maxRange = 
+        maxRangeOverride.isSet() ? *maxRangeOverride :
+        bs.radius() * _options.layout()->tileSizeFactor().value();
 
     // build the URI for the top-level paged LOD:
     std::string uri = s_makeURI( _uid, 0, 0, 0 );
@@ -549,7 +577,8 @@ FeatureModelGraph::load( unsigned lod, unsigned tileX, unsigned tileY, const std
     }
     else
     {
-        RemoveEmptyGroupsVisitor::run( result );
+        // For some unknown reason, this breaks when I insert an LOD. -gw
+        //RemoveEmptyGroupsVisitor::run( result );
     }
 
     if ( result->getNumChildren() == 0 )
@@ -633,9 +662,10 @@ FeatureModelGraph::buildLevel( const FeatureLevel& level, const GeoExtent& exten
     // set up for feature indexing if appropriate:
     osg::ref_ptr<osg::Group> group;
     FeatureSourceIndexNode* index = 0L;
-    if ( _session->getFeatureSource() && (_options.featureIndexing() == true) )
+
+    if ( _session->getFeatureSource() && _options.featureIndexing().isSet() )
     {
-        index = new FeatureSourceIndexNode( _session->getFeatureSource() );
+        index = new FeatureSourceIndexNode( _session->getFeatureSource(), *_options.featureIndexing() );
         group = index;
     }
     else
@@ -692,27 +722,24 @@ FeatureModelGraph::buildLevel( const FeatureLevel& level, const GeoExtent& exten
 
     if ( group->getNumChildren() > 0 )
     {
-        
         // account for a min-range here. Do not address the max-range here; that happens
-        // above when generating paged LOD nodes, etc.        
+        // above when generating paged LOD nodes, etc.
         float minRange = level.minRange();
 
-        /*
-        if ( _options.minRange().isSet() ) 
-            minRange = std::max(minRange, *_options.minRange());
-
-        if ( _options.layout().isSet() && _options.layout()->minRange().isSet() )
-            minRange = std::max(minRange, *_options.layout()->minRange());
-            */
-
+#if 1
         if ( minRange > 0.0f )
         {
             // minRange can't be less than the tile geometry's radius.
-            minRange = std::max(minRange, (float)group->getBound().radius());
-            osg::LOD* lod = new osg::LOD();
-            lod->addChild( group.get(), minRange, FLT_MAX );
+            //minRange = std::max(minRange, (float)group->getBound().radius());
+            //osg::LOD* lod = new osg::LOD();
+            //lod->addChild( group.get(), minRange, FLT_MAX );
+
+            ElevationLOD* lod = new ElevationLOD( _session->getMapSRS() );
+            lod->setMinElevation( minRange );
+            lod->addChild( group.get() );
             group = lod;
-        }        
+        }
+#endif
 
         if ( _session->getMapInfo().isGeocentric() && _options.clusterCulling() == true )
         {
@@ -727,8 +754,10 @@ FeatureModelGraph::buildLevel( const FeatureLevel& level, const GeoExtent& exten
                     // get the geocentric tile center:
                     osg::Vec3d tileCenter;
                     ccExtent.getCentroid( tileCenter.x(), tileCenter.y() );
+
                     osg::Vec3d centerECEF;
-                    ccExtent.getSRS()->transformToECEF( tileCenter, centerECEF );
+                    ccExtent.getSRS()->transform( tileCenter, _session->getMapSRS()->getECEF(), centerECEF );
+                    //ccExtent.getSRS()->transformToECEF( tileCenter, centerECEF );
 
                     osg::NodeCallback* ccc = ClusterCullingFactory::create2( group.get(), centerECEF );
                     if ( ccc )
@@ -785,11 +814,13 @@ FeatureModelGraph::build(const Style&        defaultStyle,
 
                 // Get the Group that parents all features of this particular style. Note, this
                 // might be NULL if the factory does not support style groups.
-                osg::Group* styleGroup = _factory->getOrCreateStyleGroup(*feature->style(), _session.get());
+                osg::Group* styleGroup = getOrCreateStyleGroupFromFactory( *feature->style() );
                 if ( styleGroup )
                 {
                     if ( !group->containsNode( styleGroup ) )
+                    {
                         group->addChild( styleGroup );
+                    }
                 }
 
                 if ( _factory->createOrUpdateNode( cursor.get(), *feature->style(), context, node ) )
@@ -1022,7 +1053,7 @@ FeatureModelGraph::createStyleGroup(const Style&         style,
         if ( _factory->createOrUpdateNode( newCursor.get(), style, context, node ) )
         {
             if ( !styleGroup )
-                styleGroup = _factory->getOrCreateStyleGroup( style, _session.get() );
+                styleGroup = getOrCreateStyleGroupFromFactory( style );
 
             // if it returned a node, add it. (it doesn't necessarily have to)
             if ( node.valid() )
@@ -1034,7 +1065,6 @@ FeatureModelGraph::createStyleGroup(const Style&         style,
 }
 
 
-
 osg::Group*
 FeatureModelGraph::createStyleGroup(const Style&        style, 
                                     const Query&        query, 
@@ -1073,18 +1103,80 @@ FeatureModelGraph::createStyleGroup(const Style&        style,
 
 
 void
+FeatureModelGraph::checkForGlobalAltitudeStyles( const Style& style )
+{
+    const AltitudeSymbol* alt = style.get<AltitudeSymbol>();
+    if ( alt )
+    {
+        if (alt->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN || 
+            alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN)
+        {
+            if ( alt->technique() == AltitudeSymbol::TECHNIQUE_GPU && !_clampable )
+            {
+                _clampable = new ClampableNode( 0L );
+                _overlayChange = OVERLAY_INSTALL_CLAMPABLE;
+            }
+
+            else if ( alt->technique() == AltitudeSymbol::TECHNIQUE_DRAPE && !_drapeable )
+            {
+                _drapeable = new DrapeableNode( 0L );
+                _overlayChange = OVERLAY_INSTALL_DRAPEABLE;
+            }
+        }
+    }
+
+    if ( _clampable )
+    {
+        // if we're using extrusion, don't perform depth offsetting:
+        const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>();
+        if ( extrusion )
+        {
+            _clampable->depthOffset().enabled() = false;
+        }
+
+        // check for explicit depth offset render settings (note, this could
+        // override the automatic disable put in place by the presence of an
+        // ExtrusionSymbol above)
+        const RenderSymbol* render = style.get<RenderSymbol>();
+        if ( render && render->depthOffset().isSet() )
+        {
+            _clampable->depthOffset() = *render->depthOffset();
+        }
+    }
+}
+
+
+osg::Group*
+FeatureModelGraph::getOrCreateStyleGroupFromFactory(const Style& style)
+{
+    osg::Group* styleGroup = _factory->getOrCreateStyleGroup( style, _session.get() );
+
+    // Check the style and see if we need to active GPU clamping. GPU clamping
+    // is currently all-or-nothing for a single FMG.
+    checkForGlobalAltitudeStyles( style );
+
+    return styleGroup;
+}
+
+
+void
 FeatureModelGraph::traverse(osg::NodeVisitor& nv)
 {
-    if ( nv.getVisitorType() == osg::NodeVisitor::EVENT_VISITOR )
+    if ( nv.getVisitorType() == nv.EVENT_VISITOR )
     {
         if ( !_pendingUpdate && (_dirty || _session->getFeatureSource()->outOfSyncWith(_revision)) )
         {
             _pendingUpdate = true;
             ADJUST_UPDATE_TRAV_COUNT( this, 1 );
         }
+
+        else if ( _overlayChange != OVERLAY_NO_CHANGE )
+        {
+            ADJUST_UPDATE_TRAV_COUNT( this, 1 );
+        }
     }
 
-    else if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR )
+    else if ( nv.getVisitorType() == nv.UPDATE_VISITOR )
     {
         if ( _pendingUpdate )
         {
@@ -1092,16 +1184,87 @@ FeatureModelGraph::traverse(osg::NodeVisitor& nv)
             _pendingUpdate = false;
             ADJUST_UPDATE_TRAV_COUNT( this, -1 );
         }
+
+        else if ( _overlayChange != OVERLAY_NO_CHANGE )
+        {
+            changeOverlay();
+            _overlayChange = OVERLAY_NO_CHANGE;
+            ADJUST_UPDATE_TRAV_COUNT( this, -1 );
+        }
     }
 
     osg::Group::traverse(nv);
 }
 
+
+void
+FeatureModelGraph::runPostMergeOperations(osg::Node* node)
+{
+    if ( _postMergeOperations.valid() )
+    {
+        for( NodeOperationVector::iterator i = _postMergeOperations->begin(); i != _postMergeOperations->end(); ++i )
+        {
+            i->get()->operator()( node );
+        }
+    }
+}
+
+
+void
+FeatureModelGraph::changeOverlay()
+{
+    if (_overlayChange == OVERLAY_INSTALL_CLAMPABLE &&
+        _clampable                                  && 
+        _clampable != _overlayInstalled )
+    {
+        runPostMergeOperations( _clampable );
+        osgEarth::replaceGroup( _overlayInstalled, _clampable );
+        _overlayInstalled   = _clampable;
+        _drapeable          = 0L;
+        _overlayPlaceholder = 0L;
+        OE_INFO << LC << "Installed clampable decorator on layer " << getName() << std::endl;
+    }
+
+    else if (
+        _overlayChange == OVERLAY_INSTALL_DRAPEABLE && 
+        _drapeable                                  && 
+        _drapeable != _overlayInstalled )
+    {
+        runPostMergeOperations( _drapeable );
+        osgEarth::replaceGroup( _overlayInstalled, _drapeable );
+        _overlayInstalled   = _drapeable;
+        _overlayPlaceholder = 0L;
+        _clampable          = 0L;
+        OE_INFO << LC << "Installed drapeable decorator on layer " << getName() << std::endl;
+    }
+
+    else if (
+        _overlayChange == OVERLAY_INSTALL_PLACEHOLDER && 
+        _overlayPlaceholder                           && 
+        _overlayPlaceholder != _overlayInstalled)
+    {
+        runPostMergeOperations( _overlayPlaceholder );
+        osgEarth::replaceGroup( _overlayInstalled, _overlayPlaceholder );
+        _overlayInstalled = _overlayPlaceholder;
+        _clampable        = 0L;
+        _drapeable        = 0L;
+        OE_INFO << LC << "Installed null decorator on layer " << getName() << std::endl;
+    }
+}
+
+
 void
 FeatureModelGraph::redraw()
 {
+    // clear it out
     removeChildren( 0, getNumChildren() );
 
+    // zero out any decorators
+    _clampable          = 0L;
+    _drapeable          = 0L;
+    _overlayPlaceholder = new osg::Group();
+    _overlayInstalled   = _overlayPlaceholder;
+
     osg::Node* node = 0;
     // if there's a display schema in place, set up for quadtree paging.
     if ( _options.layout().isSet() || _useTiledSource )
@@ -1133,20 +1296,30 @@ FeatureModelGraph::redraw()
     //If they've specified a min/max range, setup an LOD
     if ( minRange != -FLT_MAX || maxRange != FLT_MAX )
     {        
+        // todo: revisit this, make sure this is still right.
         ElevationLOD *lod = new ElevationLOD(_session->getMapInfo().getSRS(), minRange, maxRange );
         lod->addChild( node );
         node = lod;
     }
 
-    // If we want fading, install a fader.
-    if ( _options.fadeInDuration().value() > 0.0f )
+    // If we want fading, install fading.
+    if ( _options.fading().isSet() )
     {
         FadeEffect* fader = new FadeEffect();
-        fader->setFadeDuration( *_options.fadeInDuration() );
+        fader->setFadeDuration( *_options.fading()->duration() );
+        fader->setMaxRange( *_options.fading()->maxRange() );
+        fader->setAttenuationDistance( *_options.fading()->attenuationDistance() );
         fader->addChild( node );
         node = fader;
     }
 
+    // overlay placeholder. this will make it easier to 
+    // replace with a clamper/draper later if necessary
+    {
+        _overlayInstalled->addChild( node );
+        node = _overlayInstalled;
+    }
+
     addChild( node );
 
     _session->getFeatureSource()->sync( _revision );
diff --git a/src/osgEarthFeatures/FeatureModelSource b/src/osgEarthFeatures/FeatureModelSource
index d55024b..40c804d 100644
--- a/src/osgEarthFeatures/FeatureModelSource
+++ b/src/osgEarthFeatures/FeatureModelSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -22,9 +22,11 @@
 
 #include <osgEarthFeatures/Common>
 #include <osgEarthFeatures/FeatureSource>
+#include <osgEarthFeatures/FeatureSourceIndexNode>
 #include <osgEarthFeatures/FeatureDisplayLayout>
 #include <osgEarthFeatures/GeometryCompiler>
 #include <osgEarthSymbology/Style>
+#include <osgEarth/FadeEffect>
 #include <osgEarth/ModelSource>
 #include <osgEarth/Map>
 #include <osgEarth/CachePolicy>
@@ -68,9 +70,9 @@ namespace osgEarth { namespace Features
         optional<StringExpression>& featureName() { return _featureNameExpr; }
         const optional<StringExpression>& featureName() const { return _featureNameExpr; }
 
-        /** Whether to create feature indexes (default = no) */
-        optional<bool>& featureIndexing() { return _featureIndexing; }
-        const optional<bool>& featureIndexing() const { return _featureIndexing; }
+        /** Whether to create feature indexes (unset = no) */
+        optional<FeatureSourceIndexOptions>& featureIndexing() { return _featureIndexing; }
+        const optional<FeatureSourceIndexOptions>& featureIndexing() const { return _featureIndexing; }
 
         /** Whether to activate backface culling (default = yes) */
         optional<bool>& backfaceCulling() { return _backfaceCulling; }
@@ -84,15 +86,9 @@ namespace osgEarth { namespace Features
         optional<CachePolicy>& cachePolicy() { return _cachePolicy; }
         const optional<CachePolicy>& cachePolicy() const { return _cachePolicy; }
 
-        /** Time (in seconds) to fade in the graphics */
-        optional<float>& fadeInDuration() { return _fadeInDuration; }
-        const optional<float>& fadeInDuration() const { return _fadeInDuration; }
-
-    public: // deprecated
-
-        /** @deprecated - use the ConvertTypeFilter instead */
-        optional<Symbology::Geometry::Type>& geometryTypeOverride() { return _geomTypeOverride; }
-        const optional<Symbology::Geometry::Type>& geometryTypeOverride() const { return _geomTypeOverride; }
+        /** Fading properties */
+        optional<FadeOptions>& fading() { return _fading; }
+        const optional<FadeOptions>& fading() const { return _fading; }
 
     public:
         /** A live feature source instance to use. Note, this does not serialize. */
@@ -115,24 +111,21 @@ namespace osgEarth { namespace Features
     private:
         void fromConfig( const Config& conf );
 
-        optional<FeatureSourceOptions>  _featureOptions;
-        optional<FeatureDisplayLayout>  _layout;
-        optional<StringExpression>      _featureNameExpr;
-        optional<bool>                  _lit;
-        optional<double>                _maxGranularity_deg;
-        optional<bool>                  _mergeGeometry;
-        optional<bool>                  _clusterCulling;
-        optional<bool>                  _featureIndexing;
-        optional<bool>                  _backfaceCulling;
-        optional<bool>                  _alphaBlending;
-        optional<CachePolicy>           _cachePolicy;
-        optional<float>                 _fadeInDuration;
-
-        osg::ref_ptr<StyleSheet>        _styles;
-        osg::ref_ptr<FeatureSource>     _featureSource;
-
-        /** @deprecated */
-        optional<Geometry::Type>        _geomTypeOverride;
+        optional<FeatureSourceOptions>      _featureOptions;
+        optional<FeatureDisplayLayout>      _layout;
+        optional<StringExpression>          _featureNameExpr;
+        optional<bool>                      _lit;
+        optional<double>                    _maxGranularity_deg;
+        optional<bool>                      _mergeGeometry;
+        optional<bool>                      _clusterCulling;
+        optional<bool>                      _backfaceCulling;
+        optional<bool>                      _alphaBlending;
+        optional<CachePolicy>               _cachePolicy;
+        optional<FadeOptions>               _fading;
+        optional<FeatureSourceIndexOptions> _featureIndexing;
+
+        osg::ref_ptr<StyleSheet>            _styles;
+        osg::ref_ptr<FeatureSource>         _featureSource;
     };
 
 
@@ -161,7 +154,7 @@ namespace osgEarth { namespace Features
          */
         virtual osg::Group* getOrCreateStyleGroup(
             const Style& style,
-            Session*     session ) { return new osg::Group(); }
+            Session*     session );
     };
 
     
@@ -200,7 +193,7 @@ namespace osgEarth { namespace Features
 
         virtual void initialize( const osgDB::Options* dbOptions =0L );
 
-        virtual osg::Node* createNode(
+        virtual osg::Node* createNodeImplementation(
             const Map*            map,
             const osgDB::Options* dbOptions,
             ProgressCallback*     progress );
diff --git a/src/osgEarthFeatures/FeatureModelSource.cpp b/src/osgEarthFeatures/FeatureModelSource.cpp
index c15af29..51b22ec 100644
--- a/src/osgEarthFeatures/FeatureModelSource.cpp
+++ b/src/osgEarthFeatures/FeatureModelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -31,15 +31,12 @@ using namespace osgEarth::Symbology;
 
 FeatureModelSourceOptions::FeatureModelSourceOptions( const ConfigOptions& options ) :
 ModelSourceOptions ( options ),
-_geomTypeOverride  ( Geometry::TYPE_UNKNOWN ),
 _lit               ( true ),
 _maxGranularity_deg( 1.0 ),
 _mergeGeometry     ( false ),
 _clusterCulling    ( true ),
-_featureIndexing   ( false ),
 _backfaceCulling   ( true ),
-_alphaBlending     ( true ),
-_fadeInDuration    ( 0.0f )
+_alphaBlending     ( true )
 {
     fromConfig( _conf );
 }
@@ -50,28 +47,21 @@ FeatureModelSourceOptions::fromConfig( const Config& conf )
     conf.getObjIfSet( "features", _featureOptions );
     _featureSource = conf.getNonSerializable<FeatureSource>("feature_source");
 
-    conf.getObjIfSet( "styles",       _styles );
-    conf.getObjIfSet( "layout",       _layout );
-    conf.getObjIfSet( "paging",       _layout ); // backwards compat.. to be deprecated
-    conf.getObjIfSet( "feature_name", _featureNameExpr );
-    conf.getObjIfSet( "cache_policy", _cachePolicy );
+    conf.getObjIfSet( "styles",           _styles );
+    conf.getObjIfSet( "layout",           _layout );
+    conf.getObjIfSet( "paging",           _layout ); // backwards compat.. to be deprecated
+    conf.getObjIfSet( "cache_policy",     _cachePolicy );
+    conf.getObjIfSet( "fading",           _fading );
+    conf.getObjIfSet( "feature_name",     _featureNameExpr );
+    conf.getObjIfSet( "feature_indexing", _featureIndexing );
 
     conf.getIfSet( "lighting",         _lit );
     conf.getIfSet( "max_granularity",  _maxGranularity_deg );
     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" );
-    if ( gt == "line" || gt == "lines" || gt == "linestring" )
-        _geomTypeOverride = Geometry::TYPE_LINESTRING;
-    else if ( gt == "point" || gt == "pointset" || gt == "points" )
-        _geomTypeOverride = Geometry::TYPE_POINTSET;
-    else if ( gt == "polygon" || gt == "polygons" )
-        _geomTypeOverride = Geometry::TYPE_POLYGON;
+
 }
 
 Config
@@ -84,27 +74,19 @@ FeatureModelSourceOptions::getConfig() const
     {
         conf.addNonSerializable("feature_source", _featureSource.get());
     }
-    conf.updateObjIfSet( "styles",       _styles );
-    conf.updateObjIfSet( "layout",       _layout );
-    conf.updateObjIfSet( "cache_policy", _cachePolicy );
+    conf.updateObjIfSet( "styles",           _styles );
+    conf.updateObjIfSet( "layout",           _layout );
+    conf.updateObjIfSet( "cache_policy",     _cachePolicy );
+    conf.updateObjIfSet( "fading",           _fading );
+    conf.updateObjIfSet( "feature_name",     _featureNameExpr );
+    conf.updateObjIfSet( "feature_indexing", _featureIndexing );
 
     conf.updateIfSet( "lighting",         _lit );
     conf.updateIfSet( "max_granularity",  _maxGranularity_deg );
     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() ) {
-        if ( _geomTypeOverride == Geometry::TYPE_LINESTRING )
-            conf.update( "geometry_type", "line" );
-        else if ( _geomTypeOverride == Geometry::TYPE_POINTSET )
-            conf.update( "geometry_type", "point" );
-        else if ( _geomTypeOverride == Geometry::TYPE_POLYGON )
-            conf.update( "geometry_type", "polygon" );
-    }
 
     return conf;
 }
@@ -162,9 +144,9 @@ FeatureModelSource::initialize(const osgDB::Options* dbOptions)
 }
 
 osg::Node*
-FeatureModelSource::createNode(const Map*            map,
-                               const osgDB::Options* dbOptions,
-                               ProgressCallback*     progress )
+FeatureModelSource::createNodeImplementation(const Map*            map,
+                                             const osgDB::Options* dbOptions,
+                                             ProgressCallback*     progress )
 {
     // user must provide a valid map.
     if ( !map )
@@ -207,14 +189,50 @@ FeatureModelSource::createNode(const Map*            map,
     return graph;
 }
 
+
+
+//------------------------------------------------------------------------
+
+
+osg::Group*
+FeatureNodeFactory::getOrCreateStyleGroup(const Style& style,
+                                          Session*     session)
+{
+    osg::Group* group = new osg::Group();
+
+    // apply necessary render styles.
+    const RenderSymbol* render = style.get<RenderSymbol>();
+    if ( render )
+    {
+        if ( render->depthTest().isSet() )
+        {
+            group->getOrCreateStateSet()->setMode(
+                GL_DEPTH_TEST, 
+                (render->depthTest() == true ? osg::StateAttribute::ON : osg::StateAttribute::OFF) | osg::StateAttribute::OVERRIDE );
+        }
+
+        if ( render->lighting().isSet() )
+        {
+            group->getOrCreateStateSet()->setMode(
+                GL_LIGHTING,
+                (render->lighting() == true ? osg::StateAttribute::ON : osg::StateAttribute::OFF) | osg::StateAttribute::OVERRIDE );
+        }
+    }
+
+    return group;
+}
+
+
 //------------------------------------------------------------------------
+
+
 GeomFeatureNodeFactory::GeomFeatureNodeFactory( const GeometryCompilerOptions& options ) : 
 _options( options ) 
 { 
     //nop
 }
 
-bool GeomFeatureNodeFactory::createOrUpdateNode(       
+bool GeomFeatureNodeFactory::createOrUpdateNode(
     FeatureCursor*            features,
     const Style&              style,
     const FilterContext&      context,
diff --git a/src/osgEarthFeatures/FeatureSource b/src/osgEarthFeatures/FeatureSource
index 9f5726d..0d8833a 100644
--- a/src/osgEarthFeatures/FeatureSource
+++ b/src/osgEarthFeatures/FeatureSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -218,9 +218,9 @@ namespace osgEarth { namespace Features
         virtual const char* libraryName() const { return "osgEarthFeatures"; }
 
 
-		/**
-		 * Initialize the FeatureSource.
-		 */
+        /**
+         * Initialize the FeatureSource.
+         */
         virtual void initialize( const osgDB::Options* dbOptions =0L ) { }
 
         /** Dirties the feature profile */
diff --git a/src/osgEarthFeatures/FeatureSource.cpp b/src/osgEarthFeatures/FeatureSource.cpp
index b78c407..9fd8ee0 100644
--- a/src/osgEarthFeatures/FeatureSource.cpp
+++ b/src/osgEarthFeatures/FeatureSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureSourceIndexNode b/src/osgEarthFeatures/FeatureSourceIndexNode
index 2996c9e..54a4961 100644
--- a/src/osgEarthFeatures/FeatureSourceIndexNode
+++ b/src/osgEarthFeatures/FeatureSourceIndexNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -23,19 +23,42 @@
 #include <osgEarthFeatures/Common>
 #include <osgEarthFeatures/FeatureDrawSet>
 #include <osgEarthFeatures/FeatureSource>
+#include <osg/Config>
 #include <osg/Group>
 #include <osg/Drawable>
 
 namespace osgEarth { namespace Features
 {
     /**
+     * Options for a feature index
+     */
+    class OSGEARTHFEATURES_EXPORT FeatureSourceIndexOptions
+    {
+    public:
+        FeatureSourceIndexOptions(const Config& conf =Config());
+
+        /** Wheter to embed the actual Feature objects in the index (instead of
+         *  just the FeatureID). This is useful for feature sources that cannot
+         *  be queried by ID (e.g., streaming data like TFS) */
+        optional<bool>& embedFeatures() { return _embedFeatures; }
+        const optional<bool>& embedFeatures() const { return _embedFeatures; }
+
+    public:
+        Config getConfig() const;
+
+    private:
+        optional<bool> _embedFeatures;
+    };
+
+
+    /**
      * Interface for feature indexing.
      */
     class FeatureSourceIndex
     {
     public: // tagging functions
-        virtual void tagPrimitiveSets( osg::Drawable* drawable, FeatureID fid ) const =0;
-        virtual void tagNode( osg::Node* node, FeatureID fid ) const =0;
+        virtual void tagPrimitiveSets( osg::Drawable* drawable, Feature* feature ) const =0;
+        virtual void tagNode( osg::Node* node, Feature* feature ) const =0;
 
         virtual ~FeatureSourceIndex() { }
     };
@@ -45,12 +68,14 @@ namespace osgEarth { namespace Features
      * PrimitiveSets within the subgraph's geometry.
      */
     class OSGEARTHFEATURES_EXPORT FeatureSourceIndexNode : public osg::Group, public FeatureSourceIndex
-	{
-	public:
+    {
+    public:
         /**
          * Constructs a new index node.
          */
-		FeatureSourceIndexNode(FeatureSource* featureSource);
+        FeatureSourceIndexNode(
+            FeatureSource*                   featureSource,
+            const FeatureSourceIndexOptions& options );
 
         virtual ~FeatureSourceIndexNode() { }
 
@@ -60,15 +85,15 @@ namespace osgEarth { namespace Features
         /**
          * Tags all the primitive sets in a Drawable with the specified FeatureID.
          */
-        void tagPrimitiveSets( osg::Drawable* drawable, FeatureID fid ) const;
+        void tagPrimitiveSets( osg::Drawable* drawable, Feature* feature ) const;
 
         /**
          * Tags a node with the specified FeatureID.
          */
-        void tagNode( osg::Node* node, FeatureID fid ) const;
+        void tagNode( osg::Node* node, Feature* feature ) const;
 
 
-	public:
+    public:
         /**
          * The feature source tied to this node 
          */
@@ -87,7 +112,7 @@ namespace osgEarth { namespace Features
          * @param output Holds the result of the query, if returning true
          * @return true if successful
          */
-		bool getFID(osg::PrimitiveSet* pset, FeatureID& output) const;
+        bool getFID(osg::PrimitiveSet* pset, FeatureID& output) const;
 
         /**
          * Gets the Feature ID corresponding to a drawable and a prim index. This is
@@ -98,7 +123,7 @@ namespace osgEarth { namespace Features
          * @param output         Holds the result of the query, if returning true
          * @return true if successful
          */
-		bool getFID(osg::Drawable* drawable, int primitiveIndex, FeatureID& output) const;
+        bool getFID(osg::Drawable* drawable, int primitiveIndex, FeatureID& output) const;
 
         /**
          * Given a FeatureID, returns the collection of drawable/primitiveset combinations
@@ -109,7 +134,16 @@ namespace osgEarth { namespace Features
          */
         FeatureDrawSet& getDrawSet( const FeatureID& fid );
 
-	private:
+        /**
+         * Given a FeatureID, returns the cached feature.
+         *
+         * @param fid     Feature ID to look up
+         * @param output  cached feature 
+         * @return true if successful 
+         */
+        bool getFeature(const FeatureID& fid, const Feature*& output) const;
+
+    private:
         osg::ref_ptr<FeatureSource> _featureSource;
 
         typedef std::map<FeatureID, FeatureDrawSet> FeatureIDDrawSetMap;
@@ -122,11 +156,16 @@ namespace osgEarth { namespace Features
             FeatureIDDrawSetMap& _index;
             unsigned _psets;
         };
+        
+        FeatureSourceIndexOptions _options;
+
+        typedef std::map< FeatureID, osg::ref_ptr<const Feature> > FeatureMap;
+        mutable FeatureMap _features; // cache
 
     public:
         virtual const char* className() const { return "FeatureSourceIndexNode"; }
         virtual const char* libraryName() const { return "osgEarthFeatures"; }
-	};
+    };
 
 } } // namespace osgEarth::Features
 
diff --git a/src/osgEarthFeatures/FeatureSourceIndexNode.cpp b/src/osgEarthFeatures/FeatureSourceIndexNode.cpp
index 52f563b..828dcc3 100644
--- a/src/osgEarthFeatures/FeatureSourceIndexNode.cpp
+++ b/src/osgEarthFeatures/FeatureSourceIndexNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -29,6 +29,25 @@ using namespace osgEarth::Features;
 //#undef  OE_DEBUG
 //#define OE_DEBUG OE_INFO
 
+
+//-----------------------------------------------------------------------------
+
+
+FeatureSourceIndexOptions::FeatureSourceIndexOptions(const Config& conf) :
+_embedFeatures( false )
+{
+    conf.getIfSet( "embed_features", _embedFeatures );
+}
+
+Config
+FeatureSourceIndexOptions::getConfig() const
+{
+    Config conf("feature_indexing");
+    conf.addIfSet( "embed_features", _embedFeatures );
+    return conf;
+}
+
+
 //-----------------------------------------------------------------------------
 
 FeatureSourceIndexNode::Collect::Collect( FeatureIDDrawSetMap& index ) :
@@ -88,8 +107,10 @@ FeatureSourceIndexNode::Collect::apply( osg::Geode& geode )
 
 //-----------------------------------------------------------------------------
 
-FeatureSourceIndexNode::FeatureSourceIndexNode(FeatureSource* featureSource) : 
-_featureSource( featureSource )
+FeatureSourceIndexNode::FeatureSourceIndexNode(FeatureSource*                   featureSource, 
+                                               const FeatureSourceIndexOptions& options) :
+_featureSource( featureSource ), 
+_options      ( options )
 {
     //nop
 }
@@ -110,7 +131,7 @@ FeatureSourceIndexNode::reindex()
 
 // Tags all the primitive sets in a Drawable with the specified FeatureID
 void
-FeatureSourceIndexNode::tagPrimitiveSets(osg::Drawable* drawable, FeatureID fid) const
+FeatureSourceIndexNode::tagPrimitiveSets(osg::Drawable* drawable, Feature* feature) const
 {
     if ( drawable == 0L )
         return;
@@ -125,17 +146,27 @@ FeatureSourceIndexNode::tagPrimitiveSets(osg::Drawable* drawable, FeatureID fid)
     for( osg::Geometry::PrimitiveSetList::iterator p = plist.begin(); p != plist.end(); ++p )
     {
         if ( !rfid )
-            rfid = new RefFeatureID(fid);
+            rfid = new RefFeatureID(feature->getFID());
 
         p->get()->setUserData( rfid );
+
+        if ( _options.embedFeatures() == true )
+        {
+            _features[feature->getFID()] = feature;
+        }
     }
 }
 
 
 void
-FeatureSourceIndexNode::tagNode( osg::Node* node, FeatureID fid ) const
+FeatureSourceIndexNode::tagNode( osg::Node* node, Feature* feature ) const
 {
-    node->setUserData( new RefFeatureID(fid) );
+    node->setUserData( new RefFeatureID(feature->getFID()) );
+
+    if ( _options.embedFeatures() == true )
+    {
+        _features[feature->getFID()] = feature;
+    }
 }
 
 
@@ -221,3 +252,26 @@ FeatureSourceIndexNode::getDrawSet(const FeatureID& fid )
     FeatureIDDrawSetMap::iterator i = _drawSets.find(fid);
     return i != _drawSets.end() ? i->second : s_empty;
 }
+
+
+bool
+FeatureSourceIndexNode::getFeature(const FeatureID& fid, const Feature*& output) const
+{
+    if ( _options.embedFeatures() == true )
+    {
+        FeatureMap::const_iterator f = _features.find(fid);
+
+        if(f != _features.end())
+        {
+            output = f->second.get();
+            return output != 0L;
+        }
+    }
+    else if ( _featureSource.valid() )
+    {
+        output = _featureSource->getFeature( fid );
+        return output != 0L;
+    }
+
+    return false;
+}
diff --git a/src/osgEarthFeatures/FeatureTileSource b/src/osgEarthFeatures/FeatureTileSource
index 8e3f28c..0244e6c 100644
--- a/src/osgEarthFeatures/FeatureTileSource
+++ b/src/osgEarthFeatures/FeatureTileSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FeatureTileSource.cpp b/src/osgEarthFeatures/FeatureTileSource.cpp
index cb65aed..99ee5b1 100644
--- a/src/osgEarthFeatures/FeatureTileSource.cpp
+++ b/src/osgEarthFeatures/FeatureTileSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/Filter b/src/osgEarthFeatures/Filter
index 8aad80f..b4dfd95 100644
--- a/src/osgEarthFeatures/Filter
+++ b/src/osgEarthFeatures/Filter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -165,6 +165,15 @@ namespace osgEarth { namespace Features
             bool                           toECEF );
 
         void transformAndLocalize(
+            const std::vector<osg::Vec3d>& input,
+            const SpatialReference*        inputSRS,
+            osg::Vec3Array*                out_verts,
+            osg::Vec3Array*                out_normals,
+            const SpatialReference*        outputSRS,
+            const osg::Matrixd&            world2local,
+            bool                           toECEF );
+
+        void transformAndLocalize(
             const osg::Vec3d&              input,
             const SpatialReference*        inputSRS,
             osg::Vec3d&                    output,
diff --git a/src/osgEarthFeatures/Filter.cpp b/src/osgEarthFeatures/Filter.cpp
index 1244228..a7587a2 100644
--- a/src/osgEarthFeatures/Filter.cpp
+++ b/src/osgEarthFeatures/Filter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -80,8 +80,8 @@ FeaturesToNodeFilter::computeLocalizers( const FilterContext& context )
             {
                 osg::Vec3d centroid, centroidECEF;
                 geodExtent.getCentroid( centroid.x(), centroid.y() );
-                geogSRS->transformToECEF( centroid, centroidECEF );
-                _local2world = ECEF::createLocalToWorld( centroidECEF );
+                geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF );
+                geogSRS->getECEF()->createLocalToWorld( centroidECEF, _local2world );
                 _world2local.invert( _local2world );
             }
         }
@@ -117,7 +117,7 @@ FeaturesToNodeFilter::transformAndLocalize(const std::vector<osg::Vec3d>& input,
 
     if ( toECEF )
     {
-        ECEF::transformAndLocalize( input, inputSRS, output, world2local );
+        ECEF::transformAndLocalize( input, inputSRS, output, outputSRS, world2local );
     }
     else if ( inputSRS )
     {
@@ -138,6 +138,53 @@ FeaturesToNodeFilter::transformAndLocalize(const std::vector<osg::Vec3d>& input,
     }
 }
 
+
+
+void
+FeaturesToNodeFilter::transformAndLocalize(const std::vector<osg::Vec3d>& input,
+                                           const SpatialReference*        inputSRS,
+                                           osg::Vec3Array*                output_verts,
+                                           osg::Vec3Array*                output_normals,
+                                           const SpatialReference*        outputSRS,
+                                           const osg::Matrixd&            world2local,
+                                           bool                           toECEF )
+{
+    // pre-allocate enough space (performance)
+    output_verts->reserve( output_verts->size() + input.size() );
+
+    if ( output_normals )
+        output_normals->reserve( output_verts->size() );
+
+    if ( toECEF )
+    {
+        ECEF::transformAndLocalize( input, inputSRS, output_verts, output_normals, outputSRS, world2local );
+    }
+    else if ( inputSRS )
+    {
+        std::vector<osg::Vec3d> temp( input );
+        inputSRS->transform( temp, outputSRS );
+
+        for( std::vector<osg::Vec3d>::const_iterator i = temp.begin(); i != temp.end(); ++i )
+        {
+            output_verts->push_back( (*i) * world2local );
+            if ( output_normals )
+                output_normals->push_back( osg::Vec3(0,0,1) );
+        }
+    }
+    else
+    {
+        for( std::vector<osg::Vec3d>::const_iterator i = input.begin(); i != input.end(); ++i )
+        {
+            output_verts->push_back( (*i) * world2local );
+            if ( output_normals )
+                output_normals->push_back( osg::Vec3(0,0,1) );
+        }
+    }
+}
+
+
+
+
 void
 FeaturesToNodeFilter::transformAndLocalize(const osg::Vec3d&              input,
                                            const SpatialReference*        inputSRS,
@@ -148,7 +195,7 @@ FeaturesToNodeFilter::transformAndLocalize(const osg::Vec3d&              input,
 {
     if ( toECEF )
     {
-        ECEF::transformAndLocalize( input, inputSRS, output, world2local );
+        ECEF::transformAndLocalize( input, inputSRS, output, outputSRS, world2local );
     }
     else if ( inputSRS )
     {
diff --git a/src/osgEarthFeatures/FilterContext b/src/osgEarthFeatures/FilterContext
index 96ddec8..564d012 100644
--- a/src/osgEarthFeatures/FilterContext
+++ b/src/osgEarthFeatures/FilterContext
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/FilterContext.cpp b/src/osgEarthFeatures/FilterContext.cpp
index 97bd4d3..c9c943a 100644
--- a/src/osgEarthFeatures/FilterContext.cpp
+++ b/src/osgEarthFeatures/FilterContext.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -105,17 +105,18 @@ osg::Vec3d
 FilterContext::toMap( const osg::Vec3d& point ) const
 {
     osg::Vec3d world = toWorld(point);
-    if ( _isGeocentric )
-        _extent->getSRS()->transformFromECEF( world, world );
-    return world;
+    osg::Vec3d map;
+    _extent->getSRS()->transformFromWorld( world, map );
+    return map;
 }
 
 osg::Vec3d
 FilterContext::fromMap( const osg::Vec3d& point ) const
 {
     osg::Vec3d world;
-    if ( _isGeocentric )
-        _extent->getSRS()->transformToECEF( point, world );
+    _extent->getSRS()->transformToWorld( point, world );
+    //if ( _isGeocentric )
+    //    _extent->getSRS()->transformToECEF( point, world );
     return toLocal(world);
 }
 
diff --git a/src/osgEarthFeatures/GeometryCompiler b/src/osgEarthFeatures/GeometryCompiler
index 8289c8a..f7e5e75 100644
--- a/src/osgEarthFeatures/GeometryCompiler
+++ b/src/osgEarthFeatures/GeometryCompiler
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/GeometryCompiler.cpp b/src/osgEarthFeatures/GeometryCompiler.cpp
index 957d905..f35865c 100644
--- a/src/osgEarthFeatures/GeometryCompiler.cpp
+++ b/src/osgEarthFeatures/GeometryCompiler.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -22,6 +22,7 @@
 #include <osgEarthFeatures/AltitudeFilter>
 #include <osgEarthFeatures/CentroidFilter>
 #include <osgEarthFeatures/ExtrudeGeometryFilter>
+#include <osgEarthFeatures/PolygonizeLines>
 #include <osgEarthFeatures/ScatterFilter>
 #include <osgEarthFeatures/SubstituteModelFilter>
 #include <osgEarthFeatures/TessellateOperator>
@@ -43,10 +44,35 @@ using namespace osgEarth::Symbology;
 
 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();
+    /**
+     * Visitor that will exaggerate the bounding box of each Drawable
+     * in the scene graph to encompass a local high and low mark. We use this
+     * to support GPU-clamping, which will move vertex positions in the 
+     * GPU code. Since OSG is not aware of this movement, it may inadvertenly
+     * cull geometry which is actually visible.
+     */
+    struct OverlayGeometryAdjuster : public osg::NodeVisitor
+    {
+        float _low, _high;
+
+        OverlayGeometryAdjuster(float low, float high)
+            : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _low(low), _high(high) { }
+
+        void apply(osg::Geode& geode)
+        {
+            for( unsigned i=0; i<geode.getNumDrawables(); ++i )
+            {
+                osg::Drawable* d = geode.getDrawable(i);
+                osg::BoundingBox bbox = d->getBound();
+                if ( bbox.zMin() > _low )
+                    bbox.expandBy( osg::Vec3f(bbox.xMin(), bbox.yMin(), _low) );
+                if ( bbox.zMax() < _high )
+                    bbox.expandBy( osg::Vec3f(bbox.xMax(), bbox.yMax(), _high) );
+                d->setInitialBound( bbox );
+                d->dirtyBound();
+            }
+        }
+    };
 }
 
 //-----------------------------------------------------------------------
@@ -62,6 +88,7 @@ _useVertexBufferObjects( true ),
 _shaderPolicy      ( SHADERPOLICY_GENERATE )
 {
     fromConfig(_conf);
+    _useVertexBufferObjects = !Registry::capabilities().preferDisplayListsForStaticGeometry();
 }
 
 void
@@ -187,15 +214,10 @@ GeometryCompiler::compile(FeatureList&          workingSet,
         sharedCX.extent() = sharedCX.profile()->getExtent();
     }
 
-    // only localize coordinates if the map is geocentric AND the extent is
-    // less than 180 degrees.
-    bool localize = false;
-    if ( sharedCX.isGeoreferenced() )
-    {
-        const MapInfo& mi = sharedCX.getSession()->getMapInfo();
-        GeoExtent workingExtent = sharedCX.extent()->transform( sharedCX.profile()->getSRS()->getGeographicSRS() );
-        localize = mi.isGeocentric() && workingExtent.width() < 180.0;
-    }
+    // ref_ptr's to hold defaults in case we need them.
+    osg::ref_ptr<PointSymbol>   defaultPoint;
+    osg::ref_ptr<LineSymbol>    defaultLine;
+    osg::ref_ptr<PolygonSymbol> defaultPolygon;
 
     // go through the Style and figure out which filters to use.
     const PointSymbol*     point     = style.get<PointSymbol>();
@@ -228,13 +250,20 @@ GeometryCompiler::compile(FeatureList&          workingSet,
             {
             case Geometry::TYPE_LINESTRING:
             case Geometry::TYPE_RING:
-                line = s_defaultLineSymbol.get(); break;
+                defaultLine = new LineSymbol();
+                line = defaultLine.get();
+                break;
             case Geometry::TYPE_POINTSET:
-                point = s_defaultPointSymbol.get(); break;
+                defaultPoint = new PointSymbol();
+                point = defaultPoint.get();
+                break;
             case Geometry::TYPE_POLYGON:
-                polygon = s_defaultPolygonSymbol.get(); break;
-            case Geometry::TYPE_UNKNOWN: break;
-            case Geometry::TYPE_MULTI: break;
+                defaultPolygon = new PolygonSymbol();
+                polygon = defaultPolygon.get();
+                break;
+            case Geometry::TYPE_MULTI:
+            case Geometry::TYPE_UNKNOWN:
+                break;
             }
         }
     }
@@ -375,6 +404,8 @@ GeometryCompiler::compile(FeatureList&          workingSet,
         // apply per-feature naming if requested.
         if ( _options.featureName().isSet() )
             extrude.setFeatureNameExpr( *_options.featureName() );
+        if ( _options.useVertexBufferObjects().isSet())
+            extrude.useVertexBufferObjects() = *_options.useVertexBufferObjects();
 
         osg::Node* node = extrude.push( workingSet, sharedCX );
         if ( node )
@@ -383,6 +414,25 @@ GeometryCompiler::compile(FeatureList&          workingSet,
         }
     }
 
+    // polygonized lines.
+    else if ( line != 0L && line->stroke()->widthUnits() != Units::PIXELS )
+    {
+        if ( altRequired )
+        {
+            AltitudeFilter clamp;
+            clamp.setPropertiesFromStyle( style );
+            sharedCX = clamp.push( workingSet, sharedCX );
+            altRequired = false;
+        }
+
+        PolygonizeLinesFilter filter( style );
+        osg::Node* node = filter.push( workingSet, sharedCX );
+        if ( node )
+        {
+            resultGroup->addChild( node );
+        }
+    }
+
     // simple geometry
     else if ( point || line || polygon )
     {
@@ -443,19 +493,41 @@ GeometryCompiler::compile(FeatureList&          workingSet,
     {
         if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
         {
-            ShaderGenerator gen( 0L );
+            ShaderGenerator gen( 0L );  // no ss cache because we will optimize later
             resultGroup->accept( gen );
         }
         else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
         {
             resultGroup->getOrCreateStateSet()->setAttributeAndModes(
-                s_nullProgram,
+                new osg::Program(),
                 osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
         }
     }
 
     // Optimize stateset sharing.
     sscache->optimize( resultGroup.get() );
+    
+    // todo: this helps a lot, but is currently broken for non-triangle
+    // geometries. (gw, 12-17-2012)
+#if 0
+        osgUtil::Optimizer optimizer;
+        optimizer.optimize(
+            resultGroup.get(),
+            osgUtil::Optimizer::VERTEX_PRETRANSFORM );
+            osgUtil::Optimizer::VERTEX_POSTTRANSFORM );
+#endif
+
+#if 0
+    // if necessary, modify the bounding boxes of the underlying Geometry
+    // drawables so they will work with clamping.
+    if (altitude &&
+        (altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN || altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN) &&
+        altitude->technique() == AltitudeSymbol::TECHNIQUE_GPU)
+    {
+        OverlayGeometryAdjuster adjuster( -10000.0f, 10000.0f );
+        resultGroup->accept( adjuster );
+    }
+#endif
 
 
     //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" );
diff --git a/src/osgEarthFeatures/GeometryUtils b/src/osgEarthFeatures/GeometryUtils
index d05c9d4..96f2c52 100644
--- a/src/osgEarthFeatures/GeometryUtils
+++ b/src/osgEarthFeatures/GeometryUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/GeometryUtils.cpp b/src/osgEarthFeatures/GeometryUtils.cpp
index 46ba8be..236eb75 100644
--- a/src/osgEarthFeatures/GeometryUtils.cpp
+++ b/src/osgEarthFeatures/GeometryUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/LabelSource b/src/osgEarthFeatures/LabelSource
index f4cf47d..dff2a3c 100644
--- a/src/osgEarthFeatures/LabelSource
+++ b/src/osgEarthFeatures/LabelSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/LabelSource.cpp b/src/osgEarthFeatures/LabelSource.cpp
index ab0a537..23ec187 100644
--- a/src/osgEarthFeatures/LabelSource.cpp
+++ b/src/osgEarthFeatures/LabelSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/MeshClamper b/src/osgEarthFeatures/MeshClamper
index 1debd77..77d786d 100644
--- a/src/osgEarthFeatures/MeshClamper
+++ b/src/osgEarthFeatures/MeshClamper
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/MeshClamper.cpp b/src/osgEarthFeatures/MeshClamper.cpp
index 4a08325..b5add00 100644
--- a/src/osgEarthFeatures/MeshClamper.cpp
+++ b/src/osgEarthFeatures/MeshClamper.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/OgrUtils b/src/osgEarthFeatures/OgrUtils
index 519c455..8935335 100644
--- a/src/osgEarthFeatures/OgrUtils
+++ b/src/osgEarthFeatures/OgrUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
 #ifndef OSGEARTHFEATURES_FEATURE_OGR_UTILS
 #define OSGEARTHFEATURES_FEATURE_OGR_UTILS 1
 
+#include <osgEarthFeatures/Common>
 #include <osgEarthFeatures/Feature>
 #include <osgEarthSymbology/Geometry>
 #include <osgEarth/StringUtils>
@@ -29,306 +30,23 @@ using namespace osgEarth;
 using namespace osgEarth::Features;
 using namespace osgEarth::Symbology;
 
-struct OgrUtils
+struct OSGEARTHFEATURES_EXPORT OgrUtils
 {
-    static void
-    populate( OGRGeometryH geomHandle, Symbology::Geometry* target, int numPoints )
-    {
-        for( int v = numPoints-1; v >= 0; v-- ) // reverse winding.. we like ccw
-        {
-            double x=0, y=0, z=0;
-            OGR_G_GetPoint( geomHandle, v, &x, &y, &z );
-            osg::Vec3d p( x, y, z );
-            if ( target->size() == 0 || p != target->back() ) // remove dupes
-                target->push_back( p );
-        }
-    }
+    static void populate( OGRGeometryH geomHandle, Symbology::Geometry* target, int numPoints );
+    
+    static Symbology::Polygon* createPolygon( OGRGeometryH geomHandle );
+       
+    static Symbology::Geometry* createGeometry( OGRGeometryH geomHandle );
 
-    static Symbology::Polygon*
-    createPolygon( OGRGeometryH geomHandle )
-    {
-        Symbology::Polygon* output = 0L;
+    static OGRGeometryH encodePart( Geometry* geometry, OGRwkbGeometryType part_type );
 
-        int numParts = OGR_G_GetGeometryCount( geomHandle );
-        if ( numParts == 0 )
-        {
-            int numPoints = OGR_G_GetPointCount( geomHandle );
-            output = new Symbology::Polygon( numPoints );
-            populate( geomHandle, output, numPoints );
-            output->open();
-        }
-        else if ( numParts > 0 )
-        {
-            for( int p = 0; p < numParts; p++ )
-            {
-                OGRGeometryH partRef = OGR_G_GetGeometryRef( geomHandle, p );
-                int numPoints = OGR_G_GetPointCount( partRef );
-                if ( p == 0 )
-                {
-                    output = new Symbology::Polygon( numPoints );
-                    populate( partRef, output, numPoints );
-                    //output->open();
-                    output->rewind( Symbology::Ring::ORIENTATION_CCW );
-                }
-                else
-                {
-                    Symbology::Ring* hole = new Symbology::Ring( numPoints );
-                    populate( partRef, hole, numPoints );
-                    //hole->open();
-                    hole->rewind( Symbology::Ring::ORIENTATION_CW );
-                    output->getHoles().push_back( hole );
-                }
-            }
-        }
-        return output;
-    }
+    static OGRGeometryH encodeShape( Geometry* geometry, OGRwkbGeometryType shape_type, OGRwkbGeometryType part_type );    
 
-    static Symbology::Geometry*
-    createGeometry( OGRGeometryH geomHandle )
-    {
-        Symbology::Geometry* output = 0L;
-
-        OGRwkbGeometryType wkbType = OGR_G_GetGeometryType( geomHandle );        
-
-        if (
-            wkbType == wkbPolygon ||
-            wkbType == wkbPolygon25D )
-        {
-            output = createPolygon( geomHandle );
-        }
-        else if (
-            wkbType == wkbLineString ||
-            wkbType == wkbLineString25D )
-        {
-            int numPoints = OGR_G_GetPointCount( geomHandle );
-            output = new Symbology::LineString( numPoints );
-            populate( geomHandle, output, numPoints );
-        }
-        else if (
-            wkbType == wkbLinearRing )
-        {
-            int numPoints = OGR_G_GetPointCount( geomHandle );
-            output = new Symbology::Ring( numPoints );
-            populate( geomHandle, output, numPoints );
-        }
-        else if ( 
-            wkbType == wkbPoint ||
-            wkbType == wkbPoint25D )
-        {
-            int numPoints = OGR_G_GetPointCount( geomHandle );
-            output = new Symbology::PointSet( numPoints );
-            populate( geomHandle, output, numPoints );
-        }
-        else if (
-            wkbType == wkbGeometryCollection ||
-            wkbType == wkbGeometryCollection25D ||
-            wkbType == wkbMultiPoint ||
-            wkbType == wkbMultiPoint25D ||
-            wkbType == wkbMultiLineString ||
-            wkbType == wkbMultiLineString25D ||
-            wkbType == wkbMultiPolygon ||
-            wkbType == wkbMultiPolygon25D )
-        {
-            Symbology::MultiGeometry* multi = new Symbology::MultiGeometry();
-
-            int numGeoms = OGR_G_GetGeometryCount( geomHandle );
-            for( int n=0; n<numGeoms; n++ )
-            {
-                OGRGeometryH subGeomRef = OGR_G_GetGeometryRef( geomHandle, n );
-                if ( subGeomRef )
-                {
-                    Symbology::Geometry* geom = createGeometry( subGeomRef );
-                    if ( geom ) multi->getComponents().push_back( geom );
-                }
-            } 
-
-            output = multi;
-        }
-
-        return output;
-    }
-
-    static OGRGeometryH
-    encodePart( Geometry* geometry, OGRwkbGeometryType part_type )
-    {
-        OGRGeometryH part_handle = OGR_G_CreateGeometry( part_type );
-
-        for( int v = geometry->size()-1; v >= 0; v-- )
-        {
-            osg::Vec3d p = (*geometry)[v];
-            OGR_G_AddPoint( part_handle, p.x(), p.y(), p.z() );
-        }
-
-        return part_handle;
-    }
-
-
-    static OGRGeometryH
-    encodeShape( Geometry* geometry, OGRwkbGeometryType shape_type, OGRwkbGeometryType part_type )
-    {
-        OGRGeometryH shape_handle = OGR_G_CreateGeometry( shape_type );
-        if ( shape_handle )
-        {
-            GeometryIterator itr(geometry, true);
-            while (itr.hasMore())
-            {
-                Geometry* geom = itr.next();
-                OGRGeometryH part_handle = encodePart( geom, part_type );
-                if ( part_handle )
-                {
-                    OGR_G_AddGeometryDirectly( shape_handle, part_handle );
-                }
-            }
-        }
-        return shape_handle;
-    }
-
-    static OGRGeometryH createOgrGeometry(osgEarth::Symbology::Geometry* geometry, OGRwkbGeometryType requestedType = wkbUnknown)
-    {
-        if (!geometry) return NULL;
-
-        if (requestedType == wkbUnknown)
-        {
-            osgEarth::Symbology::Geometry::Type geomType = geometry->getType();
-            switch( geomType)
-            {
-            case osgEarth::Symbology::Geometry::TYPE_POLYGON:  
-                requestedType = wkbPolygon;
-                break;
-            case osgEarth::Symbology::Geometry::TYPE_POINTSET:  
-                requestedType = wkbPoint;
-                break;
-            case osgEarth::Symbology::Geometry::TYPE_LINESTRING:
-                requestedType = wkbLineString;
-                break;
-            case osgEarth::Symbology::Geometry::TYPE_RING:
-                requestedType = wkbLinearRing;
-                break;            
-            case Geometry::TYPE_UNKNOWN: break;
-            case Geometry::TYPE_MULTI: 
-                {
-                    osgEarth::Symbology::MultiGeometry* multi = dynamic_cast<MultiGeometry*>(geometry);
-                    osgEarth::Symbology::Geometry::Type componentType = multi->getComponentType();
-                    requestedType = componentType == Geometry::TYPE_POLYGON ? wkbMultiPolygon : 
-                                    componentType == Geometry::TYPE_POINTSET ? wkbMultiPoint :
-                                    componentType == Geometry::TYPE_LINESTRING ? wkbMultiLineString :
-                                    wkbNone;                    
-                }
-                break;
-            }
-        }
-
-        OGRwkbGeometryType shape_type =
-            requestedType == wkbPolygon || requestedType == wkbMultiPolygon ? wkbPolygon :
-            requestedType == wkbPolygon25D || requestedType == wkbMultiPolygon25D? wkbPolygon25D :
-            requestedType == wkbLineString || requestedType == wkbMultiLineString? wkbMultiLineString :
-            requestedType == wkbLineString25D || requestedType == wkbMultiLineString25D? wkbMultiLineString25D :
-            requestedType == wkbPoint || requestedType == wkbMultiPoint? wkbMultiPoint :
-            requestedType == wkbPoint25D || requestedType == wkbMultiPoint25D? wkbMultiPoint25D :
-            wkbNone;
-
-        OGRwkbGeometryType part_type =
-            shape_type == wkbPolygon || shape_type == wkbPolygon25D? wkbLinearRing :
-            shape_type == wkbMultiLineString? wkbLineString :
-            shape_type == wkbMultiLineString25D? wkbLineString25D :
-            shape_type == wkbMultiPoint? wkbPoint :
-            shape_type == wkbMultiPoint25D? wkbPoint25D :
-            wkbNone;
-
-        //OE_NOTICE << "shape_type = " << shape_type << " part_type=" << part_type << std::endl;
-
-
-        osgEarth::Symbology::MultiGeometry* multi = dynamic_cast<MultiGeometry*>(geometry);
-
-        if ( multi )
-        {
-            OGRGeometryH group_handle = OGR_G_CreateGeometry( wkbGeometryCollection );
-
-            for (GeometryCollection::iterator itr = multi->getComponents().begin(); itr != multi->getComponents().end(); ++itr)
-            {
-                OGRGeometryH shape_handle = encodeShape( itr->get(), shape_type, part_type );
-                if ( shape_handle )
-                {
-                    OGRErr error = OGR_G_AddGeometryDirectly( group_handle, shape_handle );
-                    if ( error != OGRERR_NONE )
-                    {
-                        OE_WARN << "OGR_G_AddGeometryDirectly failed! " << error << std::endl;
-                        OE_WARN << "shape_type = " << shape_type << " part_type=" << part_type << std::endl;
-                    }                    
-                }
-            }
-
-            return group_handle;
-        }
-        else
-        {
-            OGRGeometryH shape_handle = encodeShape( geometry, shape_type, part_type );
-            return shape_handle;
-        }
-    }
-
-    static Feature* createFeature( OGRFeatureH handle, const SpatialReference* srs )
-    {
-        long fid = OGR_F_GetFID( handle );
-
-        OGRGeometryH geomRef = OGR_F_GetGeometryRef( handle );	
-
-        Symbology::Geometry* geom = 0;
-
-        if ( geomRef )
-        {
-            geom = OgrUtils::createGeometry( geomRef );
-        }
-
-        Feature* feature = new Feature( geom, srs, Style(), fid );
-
-        int numAttrs = OGR_F_GetFieldCount(handle); 
-        for (int i = 0; i < numAttrs; ++i) 
-        { 
-            OGRFieldDefnH field_handle_ref = OGR_F_GetFieldDefnRef( handle, i ); 
-
-            // get the field name and convert to lower case:
-            const char* field_name = OGR_Fld_GetNameRef( field_handle_ref ); 
-            std::string name = std::string( field_name ); 
-            std::transform( name.begin(), name.end(), name.begin(), ::tolower ); 
-
-            // get the field type and set the value appropriately
-            OGRFieldType field_type = OGR_Fld_GetType( field_handle_ref );
-            switch( field_type )
-            {
-                case OFTInteger:
-                    {
-                        int value = OGR_F_GetFieldAsInteger( handle, i );
-                        feature->set( name, value );
-                    }
-                    break;
-                case OFTReal:
-                    {
-                        double value = OGR_F_GetFieldAsDouble( handle, i );
-                        feature->set( name, value );
-                    }
-                    break;
-                default:
-                    {
-                        const char* value = OGR_F_GetFieldAsString(handle, i);
-                        feature->set( name, std::string(value) );
-                    }
-            }
-        } 
-
-        return feature;
-    }
-
-    static AttributeType getAttributeType( OGRFieldType type )
-    {
-        switch (type)
-        {
-        case OFTString: return ATTRTYPE_STRING;
-        case OFTReal: return ATTRTYPE_DOUBLE;
-        case OFTInteger: return ATTRTYPE_INT;
-        default: return ATTRTYPE_UNSPECIFIED;
-        };        
-    }
+    static OGRGeometryH createOgrGeometry(osgEarth::Symbology::Geometry* geometry, OGRwkbGeometryType requestedType = wkbUnknown);
+    
+    static Feature* createFeature( OGRFeatureH handle, const SpatialReference* srs );
+    
+    static AttributeType getAttributeType( OGRFieldType type );    
 };
 
 
diff --git a/src/osgEarthFeatures/OgrUtils.cpp b/src/osgEarthFeatures/OgrUtils.cpp
new file mode 100644
index 0000000..610d6aa
--- /dev/null
+++ b/src/osgEarthFeatures/OgrUtils.cpp
@@ -0,0 +1,348 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osgEarthFeatures/OgrUtils>
+
+#define LC "[FeatureSource] "
+
+using namespace osgEarth::Features;
+
+
+void
+    OgrUtils::populate( OGRGeometryH geomHandle, Symbology::Geometry* target, int numPoints )
+{
+    for( int v = numPoints-1; v >= 0; v-- ) // reverse winding.. we like ccw
+    {
+        double x=0, y=0, z=0;
+        OGR_G_GetPoint( geomHandle, v, &x, &y, &z );
+        osg::Vec3d p( x, y, z );
+        if ( target->size() == 0 || p != target->back() ) // remove dupes
+            target->push_back( p );
+    }
+}
+
+Symbology::Polygon*
+    OgrUtils::createPolygon( OGRGeometryH geomHandle )
+{
+    Symbology::Polygon* output = 0L;
+
+    int numParts = OGR_G_GetGeometryCount( geomHandle );
+    if ( numParts == 0 )
+    {
+        int numPoints = OGR_G_GetPointCount( geomHandle );
+        output = new Symbology::Polygon( numPoints );
+        populate( geomHandle, output, numPoints );
+        output->open();
+    }
+    else if ( numParts > 0 )
+    {
+        for( int p = 0; p < numParts; p++ )
+        {
+            OGRGeometryH partRef = OGR_G_GetGeometryRef( geomHandle, p );
+            int numPoints = OGR_G_GetPointCount( partRef );
+            if ( p == 0 )
+            {
+                output = new Symbology::Polygon( numPoints );
+                populate( partRef, output, numPoints );
+                //output->open();
+                output->rewind( Symbology::Ring::ORIENTATION_CCW );
+            }
+            else
+            {
+                Symbology::Ring* hole = new Symbology::Ring( numPoints );
+                populate( partRef, hole, numPoints );
+                //hole->open();
+                hole->rewind( Symbology::Ring::ORIENTATION_CW );
+                output->getHoles().push_back( hole );
+            }
+        }
+    }
+    return output;
+}
+
+Symbology::Geometry*
+    OgrUtils::createGeometry( OGRGeometryH geomHandle )
+{
+    Symbology::Geometry* output = 0L;
+
+    OGRwkbGeometryType wkbType = OGR_G_GetGeometryType( geomHandle );        
+
+    if (
+        wkbType == wkbPolygon ||
+        wkbType == wkbPolygon25D )
+    {
+        output = createPolygon( geomHandle );
+    }
+    else if (
+        wkbType == wkbLineString ||
+        wkbType == wkbLineString25D )
+    {
+        int numPoints = OGR_G_GetPointCount( geomHandle );
+        output = new Symbology::LineString( numPoints );
+        populate( geomHandle, output, numPoints );
+    }
+    else if (
+        wkbType == wkbLinearRing )
+    {
+        int numPoints = OGR_G_GetPointCount( geomHandle );
+        output = new Symbology::Ring( numPoints );
+        populate( geomHandle, output, numPoints );
+    }
+    else if ( 
+        wkbType == wkbPoint ||
+        wkbType == wkbPoint25D )
+    {
+        int numPoints = OGR_G_GetPointCount( geomHandle );
+        output = new Symbology::PointSet( numPoints );
+        populate( geomHandle, output, numPoints );
+    }
+    else if (
+        wkbType == wkbGeometryCollection ||
+        wkbType == wkbGeometryCollection25D ||
+        wkbType == wkbMultiPoint ||
+        wkbType == wkbMultiPoint25D ||
+        wkbType == wkbMultiLineString ||
+        wkbType == wkbMultiLineString25D ||
+        wkbType == wkbMultiPolygon ||
+        wkbType == wkbMultiPolygon25D )
+    {
+        Symbology::MultiGeometry* multi = new Symbology::MultiGeometry();
+
+        int numGeoms = OGR_G_GetGeometryCount( geomHandle );
+        for( int n=0; n<numGeoms; n++ )
+        {
+            OGRGeometryH subGeomRef = OGR_G_GetGeometryRef( geomHandle, n );
+            if ( subGeomRef )
+            {
+                Symbology::Geometry* geom = createGeometry( subGeomRef );
+                if ( geom ) multi->getComponents().push_back( geom );
+            }
+        } 
+
+        output = multi;
+    }
+
+    return output;
+}
+
+OGRGeometryH
+    OgrUtils::encodePart( Geometry* geometry, OGRwkbGeometryType part_type )
+{
+    OGRGeometryH part_handle = OGR_G_CreateGeometry( part_type );
+
+    for( int v = geometry->size()-1; v >= 0; v-- )
+    {
+        osg::Vec3d p = (*geometry)[v];
+        OGR_G_AddPoint( part_handle, p.x(), p.y(), p.z() );
+    }
+
+    return part_handle;
+}
+
+
+OGRGeometryH
+    OgrUtils::encodeShape( Geometry* geometry, OGRwkbGeometryType shape_type, OGRwkbGeometryType part_type )
+{
+    OGRGeometryH shape_handle = OGR_G_CreateGeometry( shape_type );
+    if ( shape_handle )
+    {
+        GeometryIterator itr(geometry, true);
+        while (itr.hasMore())
+        {
+            Geometry* geom = itr.next();
+            OGRGeometryH part_handle = encodePart( geom, part_type );
+            if ( part_handle )
+            {
+                OGR_G_AddGeometryDirectly( shape_handle, part_handle );
+            }
+        }
+    }
+    return shape_handle;
+}
+
+OGRGeometryH
+    OgrUtils::createOgrGeometry(osgEarth::Symbology::Geometry* geometry, OGRwkbGeometryType requestedType)
+{
+    if (!geometry) return NULL;
+
+    if (requestedType == wkbUnknown)
+    {
+        osgEarth::Symbology::Geometry::Type geomType = geometry->getType();
+        switch( geomType)
+        {
+        case osgEarth::Symbology::Geometry::TYPE_POLYGON:  
+            requestedType = wkbPolygon;
+            break;
+        case osgEarth::Symbology::Geometry::TYPE_POINTSET:  
+            requestedType = wkbPoint;
+            break;
+        case osgEarth::Symbology::Geometry::TYPE_LINESTRING:
+            requestedType = wkbLineString;
+            break;
+        case osgEarth::Symbology::Geometry::TYPE_RING:
+            requestedType = wkbLinearRing;
+            break;            
+        case Geometry::TYPE_UNKNOWN: break;
+        case Geometry::TYPE_MULTI: 
+            {
+                osgEarth::Symbology::MultiGeometry* multi = dynamic_cast<MultiGeometry*>(geometry);
+                osgEarth::Symbology::Geometry::Type componentType = multi->getComponentType();
+                requestedType = componentType == Geometry::TYPE_POLYGON ? wkbMultiPolygon : 
+                    componentType == Geometry::TYPE_POINTSET ? wkbMultiPoint :
+                    componentType == Geometry::TYPE_LINESTRING ? wkbMultiLineString :
+                    wkbNone;                    
+            }
+            break;
+        }
+    }
+
+    OGRwkbGeometryType shape_type =
+        requestedType == wkbPolygon || requestedType == wkbMultiPolygon ? wkbPolygon :
+        requestedType == wkbPolygon25D || requestedType == wkbMultiPolygon25D? wkbPolygon25D :
+        requestedType == wkbLineString || requestedType == wkbMultiLineString? wkbMultiLineString :
+        requestedType == wkbLineString25D || requestedType == wkbMultiLineString25D? wkbMultiLineString25D :
+        requestedType == wkbPoint || requestedType == wkbMultiPoint? wkbMultiPoint :
+        requestedType == wkbPoint25D || requestedType == wkbMultiPoint25D? wkbMultiPoint25D :
+        wkbNone;
+
+    OGRwkbGeometryType part_type =
+        shape_type == wkbPolygon || shape_type == wkbPolygon25D? wkbLinearRing :
+        shape_type == wkbMultiLineString? wkbLineString :
+        shape_type == wkbMultiLineString25D? wkbLineString25D :
+        shape_type == wkbMultiPoint? wkbPoint :
+        shape_type == wkbMultiPoint25D? wkbPoint25D :
+        wkbNone;
+
+    //OE_NOTICE << "shape_type = " << shape_type << " part_type=" << part_type << std::endl;
+
+
+    osgEarth::Symbology::MultiGeometry* multi = dynamic_cast<MultiGeometry*>(geometry);
+
+    if ( multi )
+    {
+        OGRGeometryH group_handle = OGR_G_CreateGeometry( wkbGeometryCollection );
+
+        for (GeometryCollection::iterator itr = multi->getComponents().begin(); itr != multi->getComponents().end(); ++itr)
+        {
+            OGRGeometryH shape_handle = encodeShape( itr->get(), shape_type, part_type );
+            if ( shape_handle )
+            {
+                OGRErr error = OGR_G_AddGeometryDirectly( group_handle, shape_handle );
+                if ( error != OGRERR_NONE )
+                {
+                    OE_WARN << "OGR_G_AddGeometryDirectly failed! " << error << std::endl;
+                    OE_WARN << "shape_type = " << shape_type << " part_type=" << part_type << std::endl;
+                }                    
+            }
+        }
+
+        return group_handle;
+    }
+    else
+    {
+        OGRGeometryH shape_handle = encodeShape( geometry, shape_type, part_type );
+        return shape_handle;
+    }
+}
+
+Feature*
+    OgrUtils::createFeature( OGRFeatureH handle, const SpatialReference* srs )
+{
+    long fid = OGR_F_GetFID( handle );
+
+    OGRGeometryH geomRef = OGR_F_GetGeometryRef( handle );	
+
+    Symbology::Geometry* geom = 0;
+
+    if ( geomRef )
+    {
+        geom = OgrUtils::createGeometry( geomRef );
+    }
+
+    Feature* feature = new Feature( geom, srs, Style(), fid );
+
+    int numAttrs = OGR_F_GetFieldCount(handle); 
+    for (int i = 0; i < numAttrs; ++i) 
+    { 
+        OGRFieldDefnH field_handle_ref = OGR_F_GetFieldDefnRef( handle, i ); 
+
+        // get the field name and convert to lower case:
+        const char* field_name = OGR_Fld_GetNameRef( field_handle_ref ); 
+        std::string name = std::string( field_name ); 
+        std::transform( name.begin(), name.end(), name.begin(), ::tolower ); 
+
+        // get the field type and set the value appropriately
+        OGRFieldType field_type = OGR_Fld_GetType( field_handle_ref );        
+        switch( field_type )
+        {
+        case OFTInteger:
+            {     
+                if (OGR_F_IsFieldSet( handle, i ))
+                {
+                    int value = OGR_F_GetFieldAsInteger( handle, i );
+                    feature->set( name, value );                    
+                }
+                else
+                {
+                    feature->setNull( name, ATTRTYPE_INT );
+                }
+            }
+            break;
+        case OFTReal:
+            {
+                if (OGR_F_IsFieldSet( handle, i ))
+                {
+                    double value = OGR_F_GetFieldAsDouble( handle, i );
+                    feature->set( name, value );
+                }
+                else
+                {
+                    feature->setNull( name, ATTRTYPE_DOUBLE );
+                }
+            }
+            break;
+        default:
+            {
+                if (OGR_F_IsFieldSet( handle, i ))
+                {
+                    const char* value = OGR_F_GetFieldAsString(handle, i);
+                    feature->set( name, std::string(value) );
+                }
+                else
+                {
+                    feature->setNull( name, ATTRTYPE_STRING );
+                }
+            }
+        }
+    } 
+
+    return feature;
+}
+
+AttributeType OgrUtils::getAttributeType( OGRFieldType type )
+{
+    switch (type)
+    {
+    case OFTString: return ATTRTYPE_STRING;
+    case OFTReal: return ATTRTYPE_DOUBLE;
+    case OFTInteger: return ATTRTYPE_INT;
+    default: return ATTRTYPE_UNSPECIFIED;
+    };        
+}
+
+
diff --git a/src/osgEarthFeatures/OptimizerHints b/src/osgEarthFeatures/OptimizerHints
index a68d236..6298e4a 100644
--- a/src/osgEarthFeatures/OptimizerHints
+++ b/src/osgEarthFeatures/OptimizerHints
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/OptimizerHints.cpp b/src/osgEarthFeatures/OptimizerHints.cpp
index 4eb4671..3542c1b 100644
--- a/src/osgEarthFeatures/OptimizerHints.cpp
+++ b/src/osgEarthFeatures/OptimizerHints.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/PolygonizeLines b/src/osgEarthFeatures/PolygonizeLines
new file mode 100644
index 0000000..d2c9ddc
--- /dev/null
+++ b/src/osgEarthFeatures/PolygonizeLines
@@ -0,0 +1,94 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 OSGEARTHFEATURES_POLYGONIZE_LINES_H
+#define OSGEARTHFEATURES_POLYGONIZE_LINES_H 1
+
+#include <osgEarthFeatures/Common>
+#include <osgEarthFeatures/Filter>
+#include <osgEarthSymbology/Stroke>
+#include <osg/Array>
+#include <osg/Geometry>
+
+namespace osgEarth { namespace Features
+{
+    using namespace osgEarth::Symbology;
+
+    /**
+     * Triangulates a line string into a buffered geometry.
+     *
+     * The resulting geometry will retain the original line string
+     * vertices as a "spine", and will include triangles on either
+     * side of the spine that form the buffered polygons. Depending
+     * on the Stroke, the geometry may also have triangulated end caps.
+     */
+    class OSGEARTHFEATURES_EXPORT PolygonizeLinesOperator
+    {
+    public:
+        /**
+         * Construct the operator
+         * @param[in ] stroke Line rendering properties
+         */
+        PolygonizeLinesOperator(const Stroke& stoke);
+
+        /**
+         * Run the polygonizer.
+         *
+         * @param[in ] verts   Line string geometry to polygonize. The polygonizer
+         *                     will add this array to the resulting geometry.
+         * @param[in ] normals Localized normals associated with the input verts.
+         *                     Used to determine the plane in which to polygonize each
+         *                     line segment. Optional; can be NULL
+         *
+         * @return Triangulated geometry, including primitive set
+         */
+        osg::Geometry* operator()(osg::Vec3Array* verts, osg::Vec3Array* normals) const;
+
+    protected:
+        Stroke _stroke;
+        bool   _makeScalable;
+        friend class PolygonizeLinesFilter;
+    };
+
+
+
+    /**
+     * Feature Filter that generates polygonized line geometry.
+     */
+    class OSGEARTHFEATURES_EXPORT PolygonizeLinesFilter : public FeaturesToNodeFilter
+    {
+    public:
+
+        /** Constructs the filter with Style information */
+        PolygonizeLinesFilter(const Style& style);
+
+
+    public: // FeaturesToNodeFilter
+
+        /** Pushes a list of features through the filter. */
+        osg::Node* push( FeatureList& input, FilterContext& context );
+
+    protected:
+
+        Style _style;
+        bool  _makeScalable;
+    };
+
+} } // namespace osgEarth::Features
+
+#endif // OSGEARTHFEATURES_POLYGONIZE_LINES_H
diff --git a/src/osgEarthFeatures/PolygonizeLines.cpp b/src/osgEarthFeatures/PolygonizeLines.cpp
new file mode 100644
index 0000000..954425e
--- /dev/null
+++ b/src/osgEarthFeatures/PolygonizeLines.cpp
@@ -0,0 +1,570 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 <osgEarthFeatures/PolygonizeLines>
+#include <osgEarthSymbology/MeshConsolidator>
+#include <osgEarth/VirtualProgram>
+#include <osgUtil/Optimizer>
+
+#define LC "[PolygonizeLines] "
+
+using namespace osgEarth::Features;
+
+#define OV(p) "("<<p.x()<<","<<p.y()<<")"
+
+namespace
+{
+    inline osg::Vec3 normalize(const osg::Vec3& in) 
+    {
+      osg::Vec3 temp(in);
+      temp.normalize();
+      return temp;
+    }
+
+
+    typedef std::pair<osg::Vec3,osg::Vec3> Segment;
+
+    // Given two rays (point + direction vector), find the intersection
+    // of those rays in 2D space and put the result in [out]. Return true
+    // if they intersect, false if they do not.
+    bool interesctRays(const osg::Vec3& p0, const osg::Vec3& pd, // point, dir
+                       const osg::Vec3& q0, const osg::Vec3& qd, // point, dir
+                       const osg::Vec3& cp,                      // control point
+                       const osg::Vec3& normal,                  // normal at control point
+                       osg::Vec3&       out)
+    {
+        // make the conversion quats:
+        osg::Quat toLocal, toWorld;
+        toLocal.makeRotate( normal, osg::Vec3(0,0,1) );
+        toWorld.makeRotate( osg::Vec3(0,0,1), normal );
+
+        // convert the inputs:
+        osg::Vec3 p0r = toLocal*p0; //(p0-cp);
+        osg::Vec3 pdr = toLocal*pd;
+        osg::Vec3 q0r = toLocal*q0; //(q0-cp);
+        osg::Vec3 qdr = toLocal*qd;
+
+        // this epsilon will cause us to skip invalid or colinear rays.
+        const float epsilon = 0.001f;
+
+        float det = pdr.y()*qdr.x()-pdr.x()*qdr.y();
+        if ( osg::equivalent(det, 0.0f, epsilon) )
+            return false;
+
+        float u = (qdr.x()*(q0r.y()-p0r.y())+qdr.y()*(p0r.x()-q0r.x()))/det;
+        if ( u < epsilon )
+            return false;
+
+        float v = (pdr.x()*(q0r.y()-p0r.y())+pdr.y()*(p0r.x()-q0r.x()))/det;
+        if ( v < epsilon )
+            return false;
+
+        out = /*cp +*/ (toWorld * (p0r + pdr*u));
+
+        return true;
+    }
+
+    // Rotate the directional vector [in] counter-clockwise by [angle] radians
+    // and return the result in [out].
+    inline void rotate(const osg::Vec3& in, float angle, const osg::Vec3& normal, osg::Vec3& out)
+    {
+        osg::Quat rot( angle, normal );
+        out = rot * in;
+    }
+
+    // Add two triangles to an EBO vector; [side] controls the winding
+    // direction.
+    inline void addTris(std::vector<unsigned>& ebo, unsigned i, unsigned prev_i, unsigned current, int side)
+    {
+        if ( side == 0 )
+        {
+            ebo.push_back( i-1 );
+            ebo.push_back( i );
+            ebo.push_back( prev_i );
+            ebo.push_back( prev_i );
+            ebo.push_back( i );
+            ebo.push_back( current );
+        }
+        else
+        {
+            ebo.push_back( i-1 );
+            ebo.push_back( prev_i );
+            ebo.push_back( i );
+            ebo.push_back( prev_i );
+            ebo.push_back( current );
+            ebo.push_back( i );
+        }
+    }
+
+    // Add a triangle to an EBO vector; [side] control the winding
+    // direction.
+    inline void addTri(std::vector<unsigned>& ebo, unsigned i0, unsigned i1, unsigned i2, int side)
+    {
+        ebo.push_back( i0 );
+        ebo.push_back( side == 0 ? i1 : i2 );
+        ebo.push_back( side == 0 ? i2 : i1 );
+    }
+}
+
+
+PolygonizeLinesOperator::PolygonizeLinesOperator(const Stroke& stroke) :
+_stroke( stroke )
+{
+    //nop
+}
+
+
+osg::Geometry*
+PolygonizeLinesOperator::operator()(osg::Vec3Array* verts, 
+                                    osg::Vec3Array* normals) const
+{
+    // number of verts on the original line.
+    unsigned lineSize = verts->size();
+
+    // cannot generate a line with less than 2 verts.
+    if ( lineSize < 2 )
+        return 0L;
+
+    float width            = Distance(*_stroke.width(), *_stroke.widthUnits()).as(Units::METERS);
+    float halfWidth        = 0.5f * width;
+    float maxRoundingAngle = asin( _stroke.roundingRatio().get() );
+    float minPixelSize     = _stroke.minPixels().getOrUse( 0.0f );
+    bool  autoScale        = minPixelSize > 0.0f;
+
+    osg::Geometry* geom  = new osg::Geometry();
+    geom->setUseVertexBufferObjects( true );
+
+    // Add the input verts to the geometry. This forms the "spine" of the
+    // polygonized line. We need the spine so we can affect proper clamping,
+    // texturing and vertex attribution.
+    geom->setVertexArray( verts );
+
+    // Set up the normals array
+    geom->setNormalArray( normals );
+    geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
+
+    // Set up the buffering vector attribute array.
+    osg::Vec3Array* spine = 0L;
+    if ( autoScale )
+    {
+        spine = new osg::Vec3Array( *verts );
+        geom->setVertexAttribArray    ( osg::Drawable::ATTRIBUTE_6, spine );
+        geom->setVertexAttribBinding  ( osg::Drawable::ATTRIBUTE_6, osg::Geometry::BIND_PER_VERTEX );
+        geom->setVertexAttribNormalize( osg::Drawable::ATTRIBUTE_6, false );
+    }
+
+    // initialize the texture coordinates.
+    float spineLen = 0.0f;
+    osg::Vec2Array* tverts = new osg::Vec2Array( lineSize );
+    geom->setTexCoordArray( 0, tverts );
+    (*tverts)[0].set( 0.5f, 0.0f );
+    for( unsigned i=1; i<lineSize; ++i )
+    {
+        Segment   seg   ( (*verts)[i-1], (*verts)[i] );  // current segment.
+        osg::Vec3 dir = seg.second - seg.first;
+        spineLen += dir.length();
+        (*tverts)[i].set( 0.5f, spineLen * 1.0f/width );
+    }
+
+    // triangulate the points into a mesh.
+    std::vector<unsigned> ebo;
+
+    // buffer the left side:
+    unsigned  i;
+    osg::Vec3 prevBufVert;
+    osg::Vec3 prevBufVec;
+    unsigned  prevBufVertPtr;
+    unsigned  eboPtr = 0;
+    osg::Vec3 prevDir;
+    osg::Quat rot, unrot;
+
+    // iterate over both "sides" of the center line:
+    for( int s=0; s<=1; ++s )
+    {
+        // s==0 is the left side, s==1 is the right side.
+        float side = s == 0 ? -1.0f : 1.0f;
+
+        // iterate over each line segment.
+        for( i=0; i<lineSize-1; ++i )
+        {
+            // establish the normal for this point, which will help us calculate a
+            // 3D buffering vector.
+            const osg::Vec3& normal = (*normals)[i];
+
+            // calculate the directional vector of this segment.
+            Segment   seg   ( (*verts)[i], (*verts)[i+1] );
+            osg::Vec3 dir = seg.second - seg.first;
+            dir.normalize();
+
+            // the buffering vector is orthogonal to the direction vector and the normal;
+            // flip it depending on the current side.
+            osg::Vec3 bufVecUnit = (s==0) ? normal ^ dir : dir ^ normal;
+
+            // scale the buffering vector to half the stroke width.
+            osg::Vec3 bufVec = bufVecUnit * halfWidth;
+
+            // calculate the starting buffered vector.
+            osg::Vec3 bufVert = (*verts)[i] + bufVec;
+
+            if ( i == 0 )
+            {
+                // first vert-- no corner to check, just make the buffered vert.
+                verts->push_back( bufVert );
+                prevBufVert = bufVert;
+                prevBufVertPtr = verts->size() - 1;
+
+                // first tex coord:
+                tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+
+                // first normal
+                normals->push_back( (*normals)[i] );
+
+                // buffering vector.
+                if ( spine ) spine->push_back( (*verts)[i] );
+
+                // render the front end-cap.
+                if ( _stroke.lineCap() == Stroke::LINECAP_ROUND )
+                {
+                    float angle = osg::PI_2;
+                    int steps = (int)ceil(angle/maxRoundingAngle);
+                    float step = angle/(float)steps;
+                    osg::Vec3 circlevec = verts->back() - (*verts)[i];
+
+                    for( int j=1; j<=steps; ++j )
+                    {
+                        osg::Vec3 v;
+                        float a = step * (float)j;
+                        rotate( circlevec, -(side)*a, (*normals)[i], v );
+
+                        verts->push_back( (*verts)[i] + v );
+                        addTri( ebo, i, verts->size()-2, verts->size()-1, s );
+                        tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                        normals->push_back( (*normals)[i] );
+                        if ( spine ) spine->push_back( (*verts)[i] );
+                    }
+                }
+                else if ( _stroke.lineCap() == Stroke::LINECAP_SQUARE )
+                {
+                    float cornerWidth = sqrt(2.0*halfWidth*halfWidth);
+
+                    verts->push_back( verts->back() - dir*halfWidth );
+                    addTri( ebo, i, verts->size()-2, verts->size()-1, s );
+                    tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                    normals->push_back( normals->back() );
+                    if ( spine ) spine->push_back( (*verts)[i] );
+
+                    verts->push_back( (*verts)[i] - dir*halfWidth );
+                    addTri( ebo, i, verts->size()-2, verts->size()-1, s );
+                    tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                    normals->push_back( (*normals)[i] );
+                    if ( spine ) spine->push_back( (verts->back() - (*verts)[i]) * sqrt(2.0f) );
+                }
+            }
+            else
+            {
+                // does the new segment turn create a reflex angle (>180deg)?
+                float z = (prevDir ^ dir).z();
+                bool isOutside = s == 0 ? z <= 0.0 : z >= 0.0;
+                bool isInside = !isOutside;
+
+                // if this is an inside angle (or we're using mitered corners)
+                // calculate the corner point by finding the convergance of the two
+                // vectors enimating from the previous and next buffered points.
+                if ( isInside || _stroke.lineJoin() == Stroke::LINEJOIN_MITRE )
+                {
+                    // unit vector from the previous buffered vert to the current one
+                    osg::Vec3 vec1 = (*verts)[i] - (*verts)[i-1];
+                    vec1.normalize();
+
+                    // unit vector from the current buffered vert to the next one.
+                    osg::Vec3 nextBufVert = seg.second + bufVec;
+                    osg::Vec3 vec2        = (*verts)[i] - (*verts)[i+1];
+                    vec2.normalize();
+
+                    // find the 2D intersection of these two vectors. Check for the 
+                    // special case of colinearity.
+                    osg::Vec3 isect;
+                    if ( interesctRays(prevBufVert, vec1, nextBufVert, vec2, (*verts)[i], (*normals)[i], isect) )
+                        verts->push_back(isect);
+                    else
+                        verts->push_back(bufVert);
+
+                    // now that we have the current buffered point, build triangles
+                    // for *previous* segment.
+                    addTris( ebo, i, prevBufVertPtr, verts->size()-1, s );
+                    tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                    normals->push_back( (*normals)[i] );
+
+                    if ( spine ) spine->push_back( (*verts)[i] );
+                }
+
+                else if ( _stroke.lineJoin() == Stroke::LINEJOIN_ROUND )
+                {
+                    // for a rounded corner, first create the first rim point:
+                    osg::Vec3 start = (*verts)[i] + prevBufVec;
+
+                    verts->push_back( start );
+                    addTris( ebo, i, prevBufVertPtr, verts->size()-1, s );
+                    tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                    normals->push_back( (*normals)[i] );
+                    if ( spine ) spine->push_back( (*verts)[i] );
+
+                    // insert the edge-rounding points:
+                    float angle = acosf( (prevBufVec * bufVec)/(halfWidth*halfWidth) );
+                    int steps = (int)ceil(angle/maxRoundingAngle);
+                    float step = angle/(float)steps;
+                    osg::Vec3 circlevec = start - (*verts)[i];
+                    for( int j=1; j<=steps; ++j )
+                    {
+                        osg::Vec3 v;
+                        float a = step * (float)j;
+                        rotate( circlevec, side*a, (*normals)[i], v );
+
+                        verts->push_back( (*verts)[i] + v );
+                        addTri( ebo, i, verts->size()-1, verts->size()-2, s );
+                        tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                        normals->push_back( (*normals)[i] );
+
+                        if ( spine ) spine->push_back( (*verts)[i] );
+                    }
+                }
+
+                // record these for the next segment.
+                prevBufVert    = verts->back();
+                prevBufVertPtr = verts->size() - 1;
+            }
+
+            // record these for the next segment.
+            prevDir    = dir;
+            prevBufVec = bufVec;
+        }
+
+        // record the final point data.
+        verts->push_back( (*verts)[i] + prevBufVec );
+        addTris( ebo, i, prevBufVertPtr, verts->size()-1, s );
+        tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+        normals->push_back( (*normals)[i] );
+        if ( spine ) spine->push_back( (*verts)[i] );
+
+        if ( _stroke.lineCap() == Stroke::LINECAP_ROUND )
+        {
+            float angle = osg::PI_2;
+            int steps = (int)ceil(angle/maxRoundingAngle);
+            float step = angle/(float)steps;
+            osg::Vec3 circlevec = verts->back() - (*verts)[i];
+
+            // tessellate half of a rounded end camp:
+            for( int j=1; j<=steps; ++j )
+            {
+                osg::Vec3 v;
+                float a = step * (float)j;
+                rotate( circlevec, (side)*a, (*normals)[i], v );
+                verts->push_back( (*verts)[i] + v );
+                addTri( ebo, i, verts->size()-1, verts->size()-2, s );
+                tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+                normals->push_back( (*normals)[i] );
+                if ( spine ) spine->push_back( (*verts)[i] );
+            }
+        }
+        else if ( _stroke.lineCap() == Stroke::LINECAP_SQUARE )
+        {
+            float cornerWidth = sqrt(2.0*halfWidth*halfWidth);
+
+            verts->push_back( verts->back() + prevDir*halfWidth );
+            addTri( ebo, i, verts->size()-1, verts->size()-2, s );
+            tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+            normals->push_back( normals->back() );
+            if ( spine ) spine->push_back( (*verts)[i] );
+
+            verts->push_back( (*verts)[i] + prevDir*halfWidth );
+            addTri( ebo, i, verts->size()-1, verts->size()-2, s );
+            tverts->push_back( osg::Vec2f(1.0*(float)s, (*tverts)[i].y()) );
+            normals->push_back( (*normals)[i] );
+            if ( spine ) spine->push_back( (*verts)[i] );
+        }
+    }
+
+    // copy the ebo into a primitive set of appropriate size:
+    osg::DrawElements* primset =
+        verts->size() > 0xFFFF ? (osg::DrawElements*)new osg::DrawElementsUInt  ( GL_TRIANGLES ) :
+        verts->size() > 0xFF   ? (osg::DrawElements*)new osg::DrawElementsUShort( GL_TRIANGLES ) :
+                                 (osg::DrawElements*)new osg::DrawElementsUByte ( GL_TRIANGLES );
+
+    primset->reserveElements( ebo.size() );
+    for(i=0; i<ebo.size(); ++i )
+        primset->addElement( ebo[i] );
+    geom->addPrimitiveSet( primset );
+
+    // generate colors
+    {
+        osg::Vec4Array* colors = new osg::Vec4Array( verts->size() );
+        colors->assign( colors->size(), _stroke.color() );
+        geom->setColorArray( colors );
+        geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
+    }
+
+#if 0
+    //TESTING
+    osg::Image* image = osgDB::readImageFile("E:/data/textures/road.jpg");
+    osg::Texture2D* tex = new osg::Texture2D(image);
+    tex->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE );
+    tex->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT );
+    geom->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex, 1);
+#endif
+
+    return geom;
+}
+
+
+//------------------------------------------------------------------------
+
+
+PolygonizeLinesFilter::PolygonizeLinesFilter(const Style& style) :
+_style( style )
+{
+    //nop
+}
+
+
+osg::Node*
+PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx)
+{
+    // compute the coordinate localization matrices.
+    computeLocalizers( cx );
+
+    // establish some things
+    bool                    makeECEF   = false;
+    const SpatialReference* featureSRS = 0L;
+    const SpatialReference* mapSRS     = 0L;
+
+    if ( cx.isGeoreferenced() )
+    {
+        makeECEF   = cx.getSession()->getMapInfo().isGeocentric();
+        featureSRS = cx.extent()->getSRS();
+        mapSRS     = cx.getSession()->getMapInfo().getProfile()->getSRS();
+    }
+
+    // The operator we'll use to make lines into polygons.
+    const LineSymbol* line = _style.get<LineSymbol>();
+    PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() );
+
+    // Geode to hold all the geometries.
+    osg::Geode* geode = new osg::Geode();
+
+    // iterate over all features.
+    for( FeatureList::iterator i = input.begin(); i != input.end(); ++i )
+    {
+        Feature* f = i->get();
+
+        // iterate over all the feature's geometry parts. We will treat
+        // them as lines strings.
+        GeometryIterator parts( f->getGeometry(), false );
+        while( parts.hasMore() )
+        {
+            Geometry* part = parts.next();
+
+            // skip empty geometry
+            if ( part->size() == 0 )
+                continue;
+
+            // transform the geometry into the target SRS and localize it about 
+            // a local reference point.
+            osg::Vec3Array* verts   = new osg::Vec3Array();
+            osg::Vec3Array* normals = new osg::Vec3Array();
+            transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF );
+
+            // turn the lines into polygons.
+            osg::Geometry* geom = polygonize( verts, normals );
+            geode->addDrawable( geom );
+        }
+    }
+
+    // attempt to combine geometries for better performance
+    MeshConsolidator::run( *geode );
+
+    // GPU performance optimization:
+#if 0 // issue: ignores vertex attributes
+    osgUtil::Optimizer optimizer;
+    optimizer.optimize(
+        result,
+        osgUtil::Optimizer::VERTEX_PRETRANSFORM |
+        osgUtil::Optimizer::VERTEX_POSTTRANSFORM );
+#endif
+
+    // If we're auto-scaling, we need a shader
+    float minPixels = line ? line->stroke()->minPixels().getOrUse( 0.0f ) : 0.0f;
+    if ( minPixels )
+    {
+        osg::StateSet* stateSet = geode->getOrCreateStateSet();
+
+        VirtualProgram* vp = new VirtualProgram();
+        vp->setName( "osgEarth::PolygonizeLines" );
+
+        const char* vs =
+            "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
+            "attribute vec3   oe_polyline_center; \n"
+            "uniform   float  oe_polyline_scale;  \n"
+            "uniform   float  oe_polyline_min_pixels; \n"
+            "uniform   mat3   oe_WindowScaleMatrix; \n"
+
+            "void oe_polyline_scalelines(inout vec4 VertexMODEL) \n"
+            "{ \n"
+            "   if ( oe_polyline_scale != 1.0 || oe_polyline_min_pixels > 0.0 ) \n"
+            "   { \n"
+            "       vec4  center_model = vec4(oe_polyline_center*VertexMODEL.w, VertexMODEL.w); \n"
+            "       vec4  vector_model = VertexMODEL - center_model; \n"
+            "       if ( length(vector_model.xyz) > 0.0 ) \n"
+            "       { \n"
+            "           float scale = oe_polyline_scale; \n"
+
+            "           vec4 vertex_clip = gl_ModelViewProjectionMatrix * VertexMODEL; \n"
+            "           vec4 center_clip = gl_ModelViewProjectionMatrix * center_model; \n"
+            "           vec4 vector_clip = vertex_clip - center_clip; \n"
+
+            "           if ( oe_polyline_min_pixels > 0.0 ) \n"
+            "           { \n"
+            "               vec3 vector_win = oe_WindowScaleMatrix * (vertex_clip.xyz/vertex_clip.w - center_clip.xyz/center_clip.w); \n"
+            "               float min_scale = max( (0.5*oe_polyline_min_pixels)/length(vector_win.xy), 1.0 ); \n"
+            "               scale = max( scale, min_scale ); \n"
+            "           } \n"
+
+            "           VertexMODEL = center_model + vector_model*scale; \n"
+            "        } \n"
+            "    } \n"
+            "} \n";
+
+        vp->setFunction( "oe_polyline_scalelines", vs, ShaderComp::LOCATION_VERTEX_MODEL );
+        vp->addBindAttribLocation( "oe_polyline_center", osg::Drawable::ATTRIBUTE_6 );
+        stateSet->setAttributeAndModes( vp, 1 );
+
+        // add the default scaling uniform.
+        // good way to test:
+        //    osgearth_viewer earthfile --uniform oe_polyline_scale 1.0 10.0
+        osg::Uniform* scaleU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_scale");
+        scaleU->set( 1.0f );
+        stateSet->addUniform( scaleU, 1 );
+
+        // the default "min pixels" uniform.
+        osg::Uniform* minPixelsU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_min_pixels");
+        minPixelsU->set( minPixels );
+        stateSet->addUniform( minPixelsU, 1 );
+    }
+
+    return delocalize( geode );
+}
diff --git a/src/osgEarthFeatures/ResampleFilter b/src/osgEarthFeatures/ResampleFilter
index b641427..f0393bf 100644
--- a/src/osgEarthFeatures/ResampleFilter
+++ b/src/osgEarthFeatures/ResampleFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ResampleFilter.cpp b/src/osgEarthFeatures/ResampleFilter.cpp
index 196293a..9f4692e 100644
--- a/src/osgEarthFeatures/ResampleFilter.cpp
+++ b/src/osgEarthFeatures/ResampleFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ScaleFilter b/src/osgEarthFeatures/ScaleFilter
index f5ff0d6..baf1b8e 100644
--- a/src/osgEarthFeatures/ScaleFilter
+++ b/src/osgEarthFeatures/ScaleFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ScaleFilter.cpp b/src/osgEarthFeatures/ScaleFilter.cpp
index bf0574b..c83b120 100644
--- a/src/osgEarthFeatures/ScaleFilter.cpp
+++ b/src/osgEarthFeatures/ScaleFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ScatterFilter b/src/osgEarthFeatures/ScatterFilter
index 24f732f..6f48cd9 100644
--- a/src/osgEarthFeatures/ScatterFilter
+++ b/src/osgEarthFeatures/ScatterFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ScatterFilter.cpp b/src/osgEarthFeatures/ScatterFilter.cpp
index c406908..f4b263a 100644
--- a/src/osgEarthFeatures/ScatterFilter.cpp
+++ b/src/osgEarthFeatures/ScatterFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/Script b/src/osgEarthFeatures/Script
index b9ce0b4..707ca71 100644
--- a/src/osgEarthFeatures/Script
+++ b/src/osgEarthFeatures/Script
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ScriptEngine b/src/osgEarthFeatures/ScriptEngine
index 78a914a..151a191 100644
--- a/src/osgEarthFeatures/ScriptEngine
+++ b/src/osgEarthFeatures/ScriptEngine
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/ScriptEngine.cpp b/src/osgEarthFeatures/ScriptEngine.cpp
index 6c70825..d4ddc9d 100644
--- a/src/osgEarthFeatures/ScriptEngine.cpp
+++ b/src/osgEarthFeatures/ScriptEngine.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/Session b/src/osgEarthFeatures/Session
index 921b60a..66515f6 100644
--- a/src/osgEarthFeatures/Session
+++ b/src/osgEarthFeatures/Session
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -74,6 +74,9 @@ namespace osgEarth { namespace Features
          */
         const MapInfo& getMapInfo() const { return _mapInfo; }
 
+        /** Gets the SRS of the map behind this session */
+        const SpatialReference* getMapSRS() const { return _mapInfo.getSRS(); }
+
         /**
          * Gets the map related to this session. Be carefull, this is held by an osg::observer_ptr and may be NULL
          */
diff --git a/src/osgEarthFeatures/Session.cpp b/src/osgEarthFeatures/Session.cpp
index f8e1992..ce398ea 100644
--- a/src/osgEarthFeatures/Session.cpp
+++ b/src/osgEarthFeatures/Session.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/SubstituteModelFilter b/src/osgEarthFeatures/SubstituteModelFilter
index b01dc86..0d12726 100644
--- a/src/osgEarthFeatures/SubstituteModelFilter
+++ b/src/osgEarthFeatures/SubstituteModelFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/SubstituteModelFilter.cpp b/src/osgEarthFeatures/SubstituteModelFilter.cpp
index d92baf8..f31b75b 100644
--- a/src/osgEarthFeatures/SubstituteModelFilter.cpp
+++ b/src/osgEarthFeatures/SubstituteModelFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -75,9 +75,9 @@ SubstituteModelFilter::findResource(const URI&            uri,
 {
     // find the corresponding marker in the cache
     InstanceResource* instance = 0L;
-    InstanceCache::Record rec = _instanceCache.get( uri );
 
-    if ( rec.valid() )
+    InstanceCache::Record rec;
+    if ( _instanceCache.get(uri, rec) )
     {
         // found it in the cache:
         instance = rec.value();
@@ -233,7 +233,7 @@ SubstituteModelFilter::process(const FeatureList&           features,
                         // could take a shortcut and just use the current extent's local2world matrix for this,
                         // but if the tile is big enough the up vectors won't be quite right.
                         osg::Matrixd rotation;
-                        ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, rotation );
+                        ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, targetSRS, rotation );
                         mat = rotationMatrix * rotation * scaleMatrix * osg::Matrixd::translate( point ) * _world2local;
                     }
                     else
@@ -249,7 +249,7 @@ SubstituteModelFilter::process(const FeatureList&           features,
 
                     if ( context.featureIndex() && !_useDrawInstanced )
                     {
-                        context.featureIndex()->tagNode( xform, input->getFID() );
+                        context.featureIndex()->tagNode( xform, input );
                     }
 
                     // name the feature if necessary
@@ -381,7 +381,7 @@ struct ClusterVisitor : public osg::NodeVisitor
                         if ( _makeECEF )
                         {
                             osg::Matrixd rotation;
-                            ECEF::transformAndGetRotationMatrix( point, _srs, point, rotation );
+                            ECEF::transformAndGetRotationMatrix( point, _srs, point, _targetSRS, rotation );
                             mat = rotationMatrix * rotation * scaleMatrix * osg::Matrixd::translate(point) * _f2n->world2local();
                         }
                         else
@@ -406,7 +406,7 @@ struct ClusterVisitor : public osg::NodeVisitor
                             geode.addDrawable( newDrawable.get() );
 
                             if ( _cx.featureIndex() )
-                                _cx.featureIndex()->tagPrimitiveSets( newDrawable.get(), feature->getFID() );
+                                _cx.featureIndex()->tagPrimitiveSets( newDrawable.get(), feature );
                         }
                     }
 
diff --git a/src/osgEarthFeatures/TessellateOperator b/src/osgEarthFeatures/TessellateOperator
index e3f43dc..b9fc400 100644
--- a/src/osgEarthFeatures/TessellateOperator
+++ b/src/osgEarthFeatures/TessellateOperator
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/TessellateOperator.cpp b/src/osgEarthFeatures/TessellateOperator.cpp
index 3665a13..3fd393b 100644
--- a/src/osgEarthFeatures/TessellateOperator.cpp
+++ b/src/osgEarthFeatures/TessellateOperator.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -61,14 +61,16 @@ TessellateOperator::tessellateGeo( const osg::Vec3d& p0, const osg::Vec3d& p1, u
         }
         else // GEOINTERP_RHUMB_LINE
         {
-            double lat1 = osg::DegreesToRadians(p0.y()), lon1 = osg::DegreesToRadians(p1.x());
+            double lat1 = osg::DegreesToRadians(p0.y()), lon1 = osg::DegreesToRadians(p0.x());
             double lat2 = osg::DegreesToRadians(p1.y()), lon2 = osg::DegreesToRadians(p1.x());
 
-            double distance = GeoMath::rhumbDistance( lat1, lon1, lat2, lon2 );
+            double totalDistance = GeoMath::rhumbDistance( lat1, lon1, lat2, lon2 );
             double bearing  = GeoMath::rhumbBearing( lat1, lon1, lat2, lon2 );
 
+            double interpDistance = t * totalDistance;
+           
             double lat3, lon3;
-            GeoMath::rhumbDestination(lat1, lon1, bearing, distance, lat3, lon3);
+            GeoMath::rhumbDestination(lat1, lon1, bearing, interpDistance, lat3, lon3);
 
             p.set( osg::RadiansToDegrees(lon3), osg::RadiansToDegrees(lat3), p0.z() + t*zdelta );
         }
diff --git a/src/osgEarthFeatures/TextSymbolizer b/src/osgEarthFeatures/TextSymbolizer
index d2ba4ff..214c00e 100644
--- a/src/osgEarthFeatures/TextSymbolizer
+++ b/src/osgEarthFeatures/TextSymbolizer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/TextSymbolizer.cpp b/src/osgEarthFeatures/TextSymbolizer.cpp
index 77f793b..0190a3e 100644
--- a/src/osgEarthFeatures/TextSymbolizer.cpp
+++ b/src/osgEarthFeatures/TextSymbolizer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/TransformFilter b/src/osgEarthFeatures/TransformFilter
index 10ed791..5e28c27 100644
--- a/src/osgEarthFeatures/TransformFilter
+++ b/src/osgEarthFeatures/TransformFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/TransformFilter.cpp b/src/osgEarthFeatures/TransformFilter.cpp
index 16c751a..5ceb4db 100644
--- a/src/osgEarthFeatures/TransformFilter.cpp
+++ b/src/osgEarthFeatures/TransformFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/VirtualFeatureSource b/src/osgEarthFeatures/VirtualFeatureSource
index 0140e19..afdc0d3 100644
--- a/src/osgEarthFeatures/VirtualFeatureSource
+++ b/src/osgEarthFeatures/VirtualFeatureSource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthFeatures/VirtualFeatureSource.cpp b/src/osgEarthFeatures/VirtualFeatureSource.cpp
index 98bedad..de19ea3 100644
--- a/src/osgEarthFeatures/VirtualFeatureSource.cpp
+++ b/src/osgEarthFeatures/VirtualFeatureSource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/Actions b/src/osgEarthQt/Actions
index c201435..807da30 100644
--- a/src/osgEarthQt/Actions
+++ b/src/osgEarthQt/Actions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/AnnotationDialogs b/src/osgEarthQt/AnnotationDialogs
index 4d5ec85..660d8f9 100644
--- a/src/osgEarthQt/AnnotationDialogs
+++ b/src/osgEarthQt/AnnotationDialogs
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -528,7 +528,7 @@ namespace osgEarth { namespace QtGui
 
       if (_root.valid() && _mouseDown)
       {
-        _ellipseNode = new osgEarth::Annotation::EllipseNode(_mapNode, _position, _radiusMajor, _radiusMinor, _rotationAngle, _ellipseStyle, false);
+        _ellipseNode = new osgEarth::Annotation::EllipseNode(_mapNode, _position, _radiusMajor, _radiusMinor, _rotationAngle, _ellipseStyle);
         _root->addChild(_ellipseNode);
       }
     }
diff --git a/src/osgEarthQt/AnnotationDialogs.cpp b/src/osgEarthQt/AnnotationDialogs.cpp
index af877cd..e1cd32a 100644
--- a/src/osgEarthQt/AnnotationDialogs.cpp
+++ b/src/osgEarthQt/AnnotationDialogs.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -464,6 +464,7 @@ void AddPathDialog::addPoint(const osgEarth::GeoPoint& point)
     pathStyle.getOrCreate<LineSymbol>()->stroke()->width() = 2.0f;
     pathStyle.getOrCreate<LineSymbol>()->tessellation() = 20;
     pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+    pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
 
     _pathFeature = new osgEarth::Features::Feature(_pathLine, _mapNode->getMapSRS(), pathStyle);
     //_pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE;
@@ -705,7 +706,8 @@ void AddPolygonDialog::refreshFeatureNode(bool geometryOnly)
       
 
       _polyFeature->style()->getOrCreate<PolygonSymbol>()->fill()->color() = _fillColor;
-      //_polyFeature->style()->getOrCreate<AltitudeSymbol>()->clamping() = _drapeCheckbox->checkState() == Qt::Checked ? AltitudeSymbol::CLAMP_TO_TERRAIN : AltitudeSymbol::CLAMP_ABSOLUTE;
+      _polyFeature->style()->getOrCreate<AltitudeSymbol>()->clamping() = _drapeCheckbox->checkState() == Qt::Checked ? AltitudeSymbol::CLAMP_TO_TERRAIN : AltitudeSymbol::CLAMP_ABSOLUTE;
+      _polyFeature->style()->getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
     }
 
     _polyNode->setFeature(_polyFeature);
@@ -982,7 +984,15 @@ void AddEllipseDialog::addEllipse(const osgEarth::GeoPoint& position, const Line
 {
   if (!_ellipseNode.valid())
   {
-    _ellipseNode = new osgEarth::Annotation::EllipseNode(_mapNode, position, radiusMajor, radiusMinor, rotationAngle, _ellipseStyle, _drapeCheckbox->checkState() == Qt::Checked);
+    Style style = _ellipseStyle;
+    if ( _drapeCheckbox->checkState() == Qt::Checked ) {
+        style.getOrCreate<AltitudeSymbol>()->clamping()  = AltitudeSymbol::CLAMP_TO_TERRAIN;
+        style.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
+    }
+    else {
+        style.getOrCreate<AltitudeSymbol>()->clamping()  = AltitudeSymbol::CLAMP_NONE;
+    }
+    _ellipseNode = new osgEarth::Annotation::EllipseNode(_mapNode, position, radiusMajor, radiusMinor, rotationAngle, style);
     _root->addChild(_ellipseNode);
   }
   else
diff --git a/src/osgEarthQt/AnnotationListWidget b/src/osgEarthQt/AnnotationListWidget
index 4ade76f..d6384fa 100644
--- a/src/osgEarthQt/AnnotationListWidget
+++ b/src/osgEarthQt/AnnotationListWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/AnnotationListWidget.cpp b/src/osgEarthQt/AnnotationListWidget.cpp
index cf72068..7f441b7 100644
--- a/src/osgEarthQt/AnnotationListWidget.cpp
+++ b/src/osgEarthQt/AnnotationListWidget.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/AnnotationToolbar b/src/osgEarthQt/AnnotationToolbar
index 2086362..d8cb82a 100644
--- a/src/osgEarthQt/AnnotationToolbar
+++ b/src/osgEarthQt/AnnotationToolbar
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/AnnotationToolbar.cpp b/src/osgEarthQt/AnnotationToolbar.cpp
index 4b9c0f6..8acc748 100644
--- a/src/osgEarthQt/AnnotationToolbar.cpp
+++ b/src/osgEarthQt/AnnotationToolbar.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/CMakeLists.txt b/src/osgEarthQt/CMakeLists.txt
index 6d0c673..83b822c 100644
--- a/src/osgEarthQt/CMakeLists.txt
+++ b/src/osgEarthQt/CMakeLists.txt
@@ -23,6 +23,7 @@ set(LIB_MOC_HDRS
     MapCatalogWidget
     TerrainProfileGraph
     TerrainProfileWidget
+    ViewerWidget
 )
 
 # Qt resource files
@@ -61,6 +62,7 @@ SET(LIB_PUBLIC_HEADERS
     MultiViewerWidget
     TerrainProfileGraph
     TerrainProfileWidget
+    ViewWidget
     ViewerWidget
     ${LIB_UI_HDRS}
     ${LIB_QT_UIS}
@@ -85,6 +87,7 @@ ADD_LIBRARY(${LIB_NAME} SHARED
     MultiViewerWidget.cpp
     TerrainProfileGraph.cpp
     TerrainProfileWidget.cpp
+    ViewWidget.cpp
     ViewerWidget.cpp
 )
 
diff --git a/src/osgEarthQt/CollapsiblePairWidget b/src/osgEarthQt/CollapsiblePairWidget
index 36699df..42a524d 100644
--- a/src/osgEarthQt/CollapsiblePairWidget
+++ b/src/osgEarthQt/CollapsiblePairWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/CollapsiblePairWidget.cpp b/src/osgEarthQt/CollapsiblePairWidget.cpp
index f3c559b..2f3802f 100644
--- a/src/osgEarthQt/CollapsiblePairWidget.cpp
+++ b/src/osgEarthQt/CollapsiblePairWidget.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/Common b/src/osgEarthQt/Common
index 5efd9a6..665b5e0 100644
--- a/src/osgEarthQt/Common
+++ b/src/osgEarthQt/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/DataManager b/src/osgEarthQt/DataManager
index be23abf..fdf0f9a 100644
--- a/src/osgEarthQt/DataManager
+++ b/src/osgEarthQt/DataManager
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/DataManager.cpp b/src/osgEarthQt/DataManager.cpp
index 6dd6f47..b80550b 100644
--- a/src/osgEarthQt/DataManager.cpp
+++ b/src/osgEarthQt/DataManager.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/GuiActions b/src/osgEarthQt/GuiActions
index d27e834..7c983a7 100644
--- a/src/osgEarthQt/GuiActions
+++ b/src/osgEarthQt/GuiActions
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/LOSControlWidget b/src/osgEarthQt/LOSControlWidget
index 53886c6..5e0b19d 100644
--- a/src/osgEarthQt/LOSControlWidget
+++ b/src/osgEarthQt/LOSControlWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/LOSControlWidget.cpp b/src/osgEarthQt/LOSControlWidget.cpp
index d60641d..08c8979 100644
--- a/src/osgEarthQt/LOSControlWidget.cpp
+++ b/src/osgEarthQt/LOSControlWidget.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/LOSCreationDialog b/src/osgEarthQt/LOSCreationDialog
index 761d57d..21c9192 100644
--- a/src/osgEarthQt/LOSCreationDialog
+++ b/src/osgEarthQt/LOSCreationDialog
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/LOSCreationDialog.cpp b/src/osgEarthQt/LOSCreationDialog.cpp
index 64f7772..c145422 100644
--- a/src/osgEarthQt/LOSCreationDialog.cpp
+++ b/src/osgEarthQt/LOSCreationDialog.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/LayerManagerWidget b/src/osgEarthQt/LayerManagerWidget
index f3cccac..b562f18 100644
--- a/src/osgEarthQt/LayerManagerWidget
+++ b/src/osgEarthQt/LayerManagerWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/LayerManagerWidget.cpp b/src/osgEarthQt/LayerManagerWidget.cpp
index 425ab76..3f8c00e 100644
--- a/src/osgEarthQt/LayerManagerWidget.cpp
+++ b/src/osgEarthQt/LayerManagerWidget.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/MapCatalogWidget b/src/osgEarthQt/MapCatalogWidget
index dc00017..73c46c7 100644
--- a/src/osgEarthQt/MapCatalogWidget
+++ b/src/osgEarthQt/MapCatalogWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/MapCatalogWidget.cpp b/src/osgEarthQt/MapCatalogWidget.cpp
index 2a2ea62..0c946f9 100644
--- a/src/osgEarthQt/MapCatalogWidget.cpp
+++ b/src/osgEarthQt/MapCatalogWidget.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/MultiViewerWidget b/src/osgEarthQt/MultiViewerWidget
index e5cfaf7..29164b3 100644
--- a/src/osgEarthQt/MultiViewerWidget
+++ b/src/osgEarthQt/MultiViewerWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/MultiViewerWidget.cpp b/src/osgEarthQt/MultiViewerWidget.cpp
index 79629e0..49656ed 100644
--- a/src/osgEarthQt/MultiViewerWidget.cpp
+++ b/src/osgEarthQt/MultiViewerWidget.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -38,7 +38,7 @@ MultiViewerWidget::MultiViewerWidget(osg::Node* scene)
   initialize();
 
   connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
-  _timer.start(15);
+  _timer.start(20);
 }
 
 void MultiViewerWidget::initialize()
diff --git a/src/osgEarthQt/TerrainProfileGraph b/src/osgEarthQt/TerrainProfileGraph
index 242bdb7..06b362c 100644
--- a/src/osgEarthQt/TerrainProfileGraph
+++ b/src/osgEarthQt/TerrainProfileGraph
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/TerrainProfileGraph.cpp b/src/osgEarthQt/TerrainProfileGraph.cpp
index 54ebf16..af787d4 100644
--- a/src/osgEarthQt/TerrainProfileGraph.cpp
+++ b/src/osgEarthQt/TerrainProfileGraph.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/TerrainProfileWidget b/src/osgEarthQt/TerrainProfileWidget
index e249476..53d94bb 100644
--- a/src/osgEarthQt/TerrainProfileWidget
+++ b/src/osgEarthQt/TerrainProfileWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/TerrainProfileWidget.cpp b/src/osgEarthQt/TerrainProfileWidget.cpp
index fdeb971..aed2dac 100644
--- a/src/osgEarthQt/TerrainProfileWidget.cpp
+++ b/src/osgEarthQt/TerrainProfileWidget.cpp
@@ -1,5 +1,5 @@
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthQt/ViewWidget b/src/osgEarthQt/ViewWidget
new file mode 100644
index 0000000..88df14e
--- /dev/null
+++ b/src/osgEarthQt/ViewWidget
@@ -0,0 +1,79 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 OSGEARTHQT_VIEW_WIDGET_H
+#define OSGEARTHQT_VIEW_WIDGET_H 1
+
+#include <osgEarthQt/Common>
+
+#include <osgEarth/Map>
+
+#include <osgQt/GraphicsWindowQt>
+#include <osgViewer/ViewerBase>
+
+#include <QtCore/QTimer>
+
+namespace osgEarth { namespace QtGui 
+{
+    using namespace osgEarth;
+
+    /**
+     * Qt widget that encapsulates an osgViewer::View.
+     *
+     * There is nothing osgEarth-specific about this class; rather we
+     * provide it for convenience. The use case is rendering the map node
+     * (or other graph) in multiple Qt windows with a single CompositeViewer.
+     */
+    class OSGEARTHQT_EXPORT ViewWidget : public osgQt::GLWidget
+    {
+    public:
+        /**
+         * Constructs a new ViewWidget, attaching an existing view.
+         *
+         * @param[in ] view   View to attach to this widget. The widget will install
+         *             a new camera in the view. (NOTE: this widget does not take
+         *             ownership of the View. You are still respsonsile for its
+         *             destruction)
+         * @param[in ] gcToShare Graphics context to share with this new view.
+         *             Usually you should just leave this NULL
+         */
+        ViewWidget(osgViewer::View* view, osg::GraphicsContext* gcToShare =0L);
+
+        /**
+         * Access the underlying view.
+         */
+        osgViewer::View* getView() { return _view.get(); }
+
+        /** dtor */
+        virtual ~ViewWidget();
+
+    protected:
+
+        void paintEvent( QPaintEvent* );
+
+    private:
+
+        void init( osg::GraphicsContext* gc );
+        osg::GraphicsContext* createOrShareGC(osg::GraphicsContext* gc);
+
+        osg::observer_ptr<osgViewer::View> _view;
+        
+    };
+} }
+
+#endif // OSGEARTHQT_VIEW_WIDGET_H
diff --git a/src/osgEarthQt/ViewWidget.cpp b/src/osgEarthQt/ViewWidget.cpp
new file mode 100644
index 0000000..b0a139b
--- /dev/null
+++ b/src/osgEarthQt/ViewWidget.cpp
@@ -0,0 +1,97 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osgEarthQt/ViewWidget>
+#include <osgGA/StateSetManipulator>
+#include <osgQt/GraphicsWindowQt>
+#include <osgViewer/View>
+
+#include <QtGui>
+#include <QtGui/QWidget>
+
+#define LC "[ViewWidget] "
+
+using namespace osgEarth;
+using namespace osgEarth::QtGui;
+
+
+ViewWidget::ViewWidget(osgViewer::View* view, osg::GraphicsContext* gc) :
+_view( view )
+{
+    init( gc );
+}
+
+
+void
+ViewWidget::init(osg::GraphicsContext* gc)
+{
+    gc = createOrShareGC( gc );
+
+    osg::Camera* camera = _view->getCamera();
+    if ( !camera )
+    {
+        camera = new osg::Camera();
+        _view->setCamera( camera );
+    }
+    camera->setGraphicsContext( gc );
+    camera->setViewport(new osg::Viewport(0, 0, gc->getTraits()->width, gc->getTraits()->height));
+    camera->setProjectionMatrixAsPerspective( 30.0f, gc->getTraits()->width/gc->getTraits()->height, 1.0f, 10000.0f );
+}
+
+
+
+osg::GraphicsContext*
+ViewWidget::createOrShareGC(osg::GraphicsContext* gc)
+{
+    if ( !gc )
+        gc->createNewContextID();
+
+    osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
+    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(ds);
+
+    traits->readDISPLAY();
+    if (traits->displayNum<0) traits->displayNum = 0;
+
+    traits->windowDecoration = false;
+    traits->x = 0; //x();
+    traits->y = 0; //y();
+    traits->width = 100; // width();
+    traits->height = 100; //height();
+    traits->doubleBuffer = true;
+    traits->alpha = ds->getMinimumNumAlphaBits();
+    traits->stencil = ds->getMinimumNumStencilBits();
+    traits->sampleBuffers = ds->getMultiSamples();
+    traits->samples = ds->getNumMultiSamples();
+    traits->sharedContext = gc;
+    traits->inheritedWindowData = new osgQt::GraphicsWindowQt::WindowData(this);
+
+    return new osgQt::GraphicsWindowQt(traits.get());
+}
+
+
+ViewWidget::~ViewWidget()
+{
+    //nop
+}
+
+
+void
+ViewWidget::paintEvent(QPaintEvent* e)
+{
+    //nop
+}
diff --git a/src/osgEarthQt/ViewerWidget b/src/osgEarthQt/ViewerWidget
index a6db091..f657e15 100644
--- a/src/osgEarthQt/ViewerWidget
+++ b/src/osgEarthQt/ViewerWidget
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -37,6 +37,8 @@ namespace osgEarth { namespace QtGui
      */
     class OSGEARTHQT_EXPORT ViewerWidget : public osgQt::GLWidget
     {
+        Q_OBJECT;
+
     public:
         /**
          * Constructs a new ViewerWidget, creating an underlying viewer.
@@ -70,13 +72,22 @@ namespace osgEarth { namespace QtGui
 
         virtual ~ViewerWidget();
 
+    public slots:
+        
+        /**
+         * Change the underlying timer's interval
+         */
+        void setTimerInterval( int milliseconds );
+
+
     protected:
 
         QTimer _timer;
 
+        void installFrameTimer();
+
         void createViewer();
         void reconfigure( osgViewer::View* );
-        void installFrameTimer();
         void paintEvent( QPaintEvent* );
 
         osg::observer_ptr<osgViewer::ViewerBase> _viewer;
diff --git a/src/osgEarthQt/ViewerWidget.cpp b/src/osgEarthQt/ViewerWidget.cpp
index 88f2043..2779128 100644
--- a/src/osgEarthQt/ViewerWidget.cpp
+++ b/src/osgEarthQt/ViewerWidget.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -89,11 +89,21 @@ ViewerWidget::~ViewerWidget()
 }
 
 
+void
+ViewerWidget::setTimerInterval(int milliseconds)
+{
+    if ( _timer.interval() != milliseconds )
+    {
+        _timer.start( milliseconds );
+    }
+}
+
+
 void ViewerWidget::installFrameTimer()
 {    
     // start the frame timer.
     connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
-    _timer.start(15);
+    _timer.start(20);
 }
 
 
diff --git a/src/osgEarthSymbology/AltitudeSymbol b/src/osgEarthSymbology/AltitudeSymbol
index 9430ea0..6cdbe79 100644
--- a/src/osgEarthSymbology/AltitudeSymbol
+++ b/src/osgEarthSymbology/AltitudeSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -32,7 +32,7 @@ namespace osgEarth { namespace Symbology
     {
     public:
         /**
-         * Controls if/how models are clamped to the terrin
+         * Controls if/how models are clamped to the terrain
          */
         enum Clamping
         {
@@ -43,26 +43,66 @@ namespace osgEarth { namespace Symbology
                 ignoring and discarding the feature's original Z value. */
             CLAMP_TO_TERRAIN,
 
+            /** Sample the terrain under the point, and ADD the terrain height to the feature's 
+                pre-existing Z value. */
+            CLAMP_RELATIVE_TO_TERRAIN,
+
             /** The feature has a attribute value that describes its height above "height zero",
                 which is typically the ellipsoid or MSL. While this will not actually modify the
                 feature's geometry, it will install ... */
-            CLAMP_ABSOLUTE,
+            CLAMP_ABSOLUTE
+        };
 
-            /** Sample the terrain under the point, and ADD the terrain height to the feature's 
-                pre-existing Z value. */
-            CLAMP_RELATIVE_TO_TERRAIN
+        /**
+         * Clamping technique - when and where to do clamping.
+         */
+        enum Technique
+        {
+            /** Clamp geometry to the map model's elevation data. */
+            TECHNIQUE_MAP,
+
+            /** Clamp geometry to the terrain's scene graph. */
+            TECHNIQUE_SCENE,
+
+            /** Clamp geometry to the terrain as they are rendered by the GPU. */
+            TECHNIQUE_GPU,
+
+            /** Clamp geometry at draw time using projective texturing. */
+            TECHNIQUE_DRAPE
+        };
+
+        /**
+         * Binding - at what granularity the clamping is performed
+         */
+        enum Binding
+        {
+            /** Clamp every vertex independently */
+            BINDING_VERTEX,
+
+            /** Clamp to the centroid of the entire geometry */
+            BINDING_CENTROID
         };
 
     public:
+        META_Symbol(AltitudeSymbol);
+
         AltitudeSymbol( const Config& conf =Config() );
 
         /** How to clamp instances to the terrain (default is CLAMP_NONE) */
         optional<Clamping>& clamping() { return _clamping; }
         const optional<Clamping>& clamping() const { return _clamping; }
 
+        /** Method of terrain following to use (default is TECHNIQUE_MAP) */
+        optional<Technique>& technique() { return _technique; }
+        const optional<Technique>& technique() const { return _technique; }
+
         /** Terrain resolution at which to perform clamping (in map units) */
-        optional<float>& clampingResolution() { return _clampingResolution; }
-        const optional<float>& clampingResolution() const { return _clampingResolution; }
+        optional<float>& clampingResolution() { return _resolution; }
+        const optional<float>& clampingResolution() const { return _resolution; }
+
+        /** Granularity at which to clamp a geometry */
+        optional<Binding>& binding() { return _binding; }
+        const optional<Binding>& binding() const { return _binding; }
 
         /** Vertical offset of geometry after clamping */
         optional<NumericExpression>& verticalOffset() { return _verticalOffset; }
@@ -75,12 +115,15 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         virtual ~AltitudeSymbol() { }
 
         optional<Clamping>           _clamping;
-        optional<float>              _clampingResolution;
+        optional<Technique>          _technique;
+        optional<Binding>            _binding;
+        optional<float>              _resolution;
         optional<NumericExpression>  _verticalOffset;
         optional<NumericExpression>  _verticalScale;
     };
diff --git a/src/osgEarthSymbology/AltitudeSymbol.cpp b/src/osgEarthSymbology/AltitudeSymbol.cpp
index 7e9e4ad..b8a303e 100644
--- a/src/osgEarthSymbology/AltitudeSymbol.cpp
+++ b/src/osgEarthSymbology/AltitudeSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/AltitudeSymbol>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -24,7 +25,9 @@ using namespace osgEarth::Symbology;
 AltitudeSymbol::AltitudeSymbol( const Config& conf ) :
 Symbol             ( conf ),
 _clamping          ( CLAMP_NONE ),
-_clampingResolution( 0.0f ),
+_technique         ( TECHNIQUE_MAP ),
+_binding           ( BINDING_VERTEX ),
+_resolution        ( 0.001f ),
 _verticalScale     ( NumericExpression(1.0) ),
 _verticalOffset    ( NumericExpression(0.0) )
 {
@@ -36,13 +39,22 @@ AltitudeSymbol::getConfig() const
 {
     Config conf;
     conf.key() = "altitude";
-    conf.addIfSet   ( "clamping",  "none",     _clamping, CLAMP_NONE );
-    conf.addIfSet   ( "clamping",  "terrain",  _clamping, CLAMP_TO_TERRAIN );
-    conf.addIfSet   ( "clamping",  "absolute", _clamping, CLAMP_ABSOLUTE );
-    conf.addIfSet   ( "clamping",  "relative", _clamping, CLAMP_RELATIVE_TO_TERRAIN );
-    conf.addIfSet   ( "clamping_resolution",   _clampingResolution );
-    conf.addObjIfSet( "vertical_offset",       _verticalOffset );
-    conf.addObjIfSet( "vertical_scale",        _verticalScale );
+    conf.addIfSet   ( "clamping",  "none",       _clamping, CLAMP_NONE );
+    conf.addIfSet   ( "clamping",  "terrain",    _clamping, CLAMP_TO_TERRAIN );
+    conf.addIfSet   ( "clamping",  "absolute",   _clamping, CLAMP_ABSOLUTE );
+    conf.addIfSet   ( "clamping",  "relative",   _clamping, CLAMP_RELATIVE_TO_TERRAIN );
+
+    conf.addIfSet   ( "technique", "map",   _technique, TECHNIQUE_MAP );
+    conf.addIfSet   ( "technique", "scene", _technique, TECHNIQUE_SCENE );
+    conf.addIfSet   ( "technique", "gpu",   _technique, TECHNIQUE_GPU );
+    conf.addIfSet   ( "technique", "drape", _technique, TECHNIQUE_DRAPE );
+
+    conf.addIfSet   ( "binding", "vertex",   _binding, BINDING_VERTEX );
+    conf.addIfSet   ( "binding", "centroid", _binding, BINDING_CENTROID );
+
+    conf.addIfSet   ( "clamping_resolution",     _resolution );
+    conf.addObjIfSet( "vertical_offset",         _verticalOffset );
+    conf.addObjIfSet( "vertical_scale",          _verticalScale );
     return conf;
 }
 
@@ -53,7 +65,66 @@ AltitudeSymbol::mergeConfig( const Config& conf )
     conf.getIfSet   ( "clamping",  "terrain",  _clamping, CLAMP_TO_TERRAIN );
     conf.getIfSet   ( "clamping",  "absolute", _clamping, CLAMP_ABSOLUTE );
     conf.getIfSet   ( "clamping",  "relative", _clamping, CLAMP_RELATIVE_TO_TERRAIN );
-    conf.getIfSet   ( "clamping_resolution",   _clampingResolution );
+
+    conf.getIfSet   ( "technique", "map",   _technique, TECHNIQUE_MAP );
+    conf.getIfSet   ( "technique", "scene", _technique, TECHNIQUE_SCENE );
+    conf.getIfSet   ( "technique", "gpu",   _technique, TECHNIQUE_GPU );
+    conf.getIfSet   ( "technique", "drape", _technique, TECHNIQUE_DRAPE );
+
+    conf.getIfSet   ( "binding", "vertex",   _binding, BINDING_VERTEX );
+    conf.getIfSet   ( "binding", "centroid", _binding, BINDING_CENTROID );
+
+    conf.getIfSet   ( "clamping_resolution",   _resolution );
     conf.getObjIfSet( "vertical_offset",       _verticalOffset );
     conf.getObjIfSet( "vertical_scale",        _verticalScale );
 }
+
+void
+AltitudeSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "altitude-clamping") ) {
+        if      ( match(c.value(), "none") )     
+            style.getOrCreate<AltitudeSymbol>()->clamping() = CLAMP_NONE;
+        else if ( match(c.value(), "terrain") )  
+            style.getOrCreate<AltitudeSymbol>()->clamping() = CLAMP_TO_TERRAIN;
+        else if ( match(c.value(), "absolute") ) 
+            style.getOrCreate<AltitudeSymbol>()->clamping() = CLAMP_ABSOLUTE;
+        else if ( match(c.value(), "relative") ) 
+            style.getOrCreate<AltitudeSymbol>()->clamping() = CLAMP_RELATIVE_TO_TERRAIN;
+        else if ( match(c.value(), "terrain-drape") )
+        {
+            style.getOrCreate<AltitudeSymbol>()->clamping()  = CLAMP_TO_TERRAIN;
+            style.getOrCreate<AltitudeSymbol>()->technique() = TECHNIQUE_DRAPE;
+        }
+        else if ( match(c.value(), "terrain-gpu") )
+        {
+            style.getOrCreate<AltitudeSymbol>()->clamping()  = CLAMP_TO_TERRAIN;
+            style.getOrCreate<AltitudeSymbol>()->technique() = TECHNIQUE_GPU;
+        }
+    }
+    else if ( match(c.key(), "altitude-technique") ) {
+        if      ( match(c.value(), "map") )
+            style.getOrCreate<AltitudeSymbol>()->technique() = TECHNIQUE_MAP;
+        else if ( match(c.value(), "scene") )
+            style.getOrCreate<AltitudeSymbol>()->technique() = TECHNIQUE_SCENE;
+        else if ( match(c.value(), "gpu") )
+            style.getOrCreate<AltitudeSymbol>()->technique() = TECHNIQUE_GPU;
+        else if ( match(c.value(), "drape") )
+            style.getOrCreate<AltitudeSymbol>()->technique() = TECHNIQUE_DRAPE;
+    }
+    else if ( match(c.key(), "altitude-binding") ) {
+        if      ( match(c.value(), "vertex") )
+            style.getOrCreate<AltitudeSymbol>()->binding() = BINDING_VERTEX;
+        else if ( match(c.value(), "centroid") )
+            style.getOrCreate<AltitudeSymbol>()->binding() = BINDING_CENTROID;
+    }
+    else if ( match(c.key(), "altitude-resolution") ) {
+        style.getOrCreate<AltitudeSymbol>()->clampingResolution() = as<float>( c.value(), 0.0f );
+    }
+    else if ( match(c.key(), "altitude-offset") ) {
+        style.getOrCreate<AltitudeSymbol>()->verticalOffset() = NumericExpression( c.value() );
+    }
+    else if ( match(c.key(), "altitude-scale") ) {
+        style.getOrCreate<AltitudeSymbol>()->verticalScale() = NumericExpression( c.value() );
+    }
+}
diff --git a/src/osgEarthSymbology/CMakeLists.txt b/src/osgEarthSymbology/CMakeLists.txt
index 2ff5716..0a922e4 100644
--- a/src/osgEarthSymbology/CMakeLists.txt
+++ b/src/osgEarthSymbology/CMakeLists.txt
@@ -20,6 +20,7 @@ SET(LIB_PUBLIC_HEADERS
     CssUtils
     Expression
     ExtrusionSymbol
+    Fill
     Geometry
     GeometryFactory
     GEOS
@@ -38,15 +39,16 @@ SET(LIB_PUBLIC_HEADERS
     PointSymbol
     PolygonSymbol
     Query
+    RenderSymbol
     Resource
     ResourceCache
     ResourceLibrary
     Skins
     StencilVolumeNode
+    Stroke
     Style
     StyleSelector
     StyleSheet
-    SLD
     Symbol
     Tags
     TextSymbol
@@ -60,6 +62,7 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     CssUtils.cpp
     Expression.cpp
     ExtrusionSymbol.cpp
+    Fill.cpp
     Geometry.cpp
     GeometryFactory.cpp
     GEOS.cpp
@@ -78,15 +81,16 @@ ADD_LIBRARY(${LIB_NAME} ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC}
     PointSymbol.cpp
     PolygonSymbol.cpp
     Query.cpp
+    RenderSymbol.cpp
     Resource.cpp
     ResourceCache.cpp
     ResourceLibrary.cpp
     Skins.cpp
     StencilVolumeNode.cpp
+    Stroke.cpp
     Style.cpp
     StyleSelector.cpp
     StyleSheet.cpp
-    SLD.cpp
     Symbol.cpp
     TextSymbol.cpp
 )
diff --git a/src/osgEarthSymbology/Color b/src/osgEarthSymbology/Color
index e658327..dd3cbbd 100644
--- a/src/osgEarthSymbology/Color
+++ b/src/osgEarthSymbology/Color
@@ -1,6 +1,6 @@
 /* --*-c++-*-- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -52,8 +52,8 @@ namespace osgEarth { namespace Symbology
         Color( float r, float g, float b, float a = 1.0f )
             : osg::Vec4f(r, g, b, a) { }
 
-        /** RGBA constructor */
-        Color( unsigned rgba );
+        /** RGBA/ABGR constructor */
+        Color( unsigned value, Format format =RGBA );
 
         /**
          * Construct a color from a hex string in one of the following formats, (with or
@@ -69,6 +69,9 @@ namespace osgEarth { namespace Symbology
         /** Encode the color at an HTML color string (e.g., "#FF004F78") */
         std::string toHTML( Format format =RGBA ) const;
 
+        /** Dump out the color as a 32-bit integer */
+        unsigned as( Format format ) const;
+
         /** Lighten/darken the color by factor */
         Color brightness( float factor ) const;
 
diff --git a/src/osgEarthSymbology/Color.cpp b/src/osgEarthSymbology/Color.cpp
index c287232..acb866e 100644
--- a/src/osgEarthSymbology/Color.cpp
+++ b/src/osgEarthSymbology/Color.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -95,13 +95,24 @@ Color Color::Magenta  ( 0xc000c0ff );
 Color Color::Cyan     ( 0x00ffffff );
 Color Color::Brown    ( 0xaa5500ff );
 
-Color::Color( unsigned rgba )
+Color::Color( unsigned v, Format format )
 {
-    set(
-        (float)(rgba>>24)/255.0f, 
-        (float)((rgba&0xFF0000)>>16)/255.0f, 
-        (float)((rgba&0xFF00)>>8)/255.0f,
-        (float)(rgba&0xFF)/255.0f );
+    if ( format == RGBA )
+    {
+        set(
+            (float)(v>>24)/255.0f, 
+            (float)((v&0xFF0000)>>16)/255.0f, 
+            (float)((v&0xFF00)>>8)/255.0f,
+            (float)(v&0xFF)/255.0f );
+    }
+    else // format == ABGR
+    {
+        set(
+            (float)(v&0xFF)/255.0f,
+            (float)((v&0xFF00)>>8)/255.0f,
+            (float)((v&0xFF0000)>>16)/255.0f, 
+            (float)(v>>24)/255.0f );
+    }
 }
 
 Color::Color( const Color& rhs, float a ) :
@@ -159,33 +170,37 @@ Color::toHTML( Format format ) const
         w = a(), x = b(), y = g(), z = r();
     }
 
-#if 1
     return Stringify()
         << "#"
         << std::hex << std::setw(2) << std::setfill('0') << (int)(w*255.0f)
         << std::hex << std::setw(2) << std::setfill('0') << (int)(x*255.0f)
         << std::hex << std::setw(2) << std::setfill('0') << (int)(y*255.0f)
         << std::hex << std::setw(2) << std::setfill('0') << (int)(z*255.0f);
-#else
-    return Stringify()
-        << "#"
-        << std::hex << std::setw(2) << std::setfill('0')
-        << (int)(w*255.0f)
-        << (int)(x*255.0f)
-        << (int)(y*255.0f)
-        << (int)(z*255.0f);
-#endif
 }
 
 Color
 Color::brightness( float perc ) const
 {
-#if 0
-    Color c( *this );
-    rgb2hsv( c );
-    c.b() = osg::clampBetween( perc * c.b(), 0.0f, 1.0f );
-    hsv2rgb( c );
-    return c;
-#endif
     return Color(r()*perc, g()*perc, b()*perc, a());
 }
+
+unsigned
+Color::as( Format format ) const
+{
+    if ( format == RGBA )
+    {
+        return 
+            (((unsigned)(r()*255.0)) << 24) |
+            (((unsigned)(g()*255.0)) << 16) |
+            (((unsigned)(b()*255.0)) << 8 ) |
+            (((unsigned)(a()*255.0)));
+    }
+    else // format == ABGR
+    {
+        return 
+            (((unsigned)(a()*255.0)) << 24) |
+            (((unsigned)(b()*255.0)) << 16) |
+            (((unsigned)(g()*255.0)) << 8 ) |
+            (((unsigned)(r()*255.0)));
+    }
+}
diff --git a/src/osgEarthSymbology/Common b/src/osgEarthSymbology/Common
index a961a32..f25f83d 100644
--- a/src/osgEarthSymbology/Common
+++ b/src/osgEarthSymbology/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/CssUtils b/src/osgEarthSymbology/CssUtils
index aa8463b..3952a76 100644
--- a/src/osgEarthSymbology/CssUtils
+++ b/src/osgEarthSymbology/CssUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/CssUtils.cpp b/src/osgEarthSymbology/CssUtils.cpp
index 795d7f4..3367aaf 100644
--- a/src/osgEarthSymbology/CssUtils.cpp
+++ b/src/osgEarthSymbology/CssUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/Expression b/src/osgEarthSymbology/Expression
index 6646afc..581f5e7 100644
--- a/src/osgEarthSymbology/Expression
+++ b/src/osgEarthSymbology/Expression
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/Expression.cpp b/src/osgEarthSymbology/Expression.cpp
index 2cc18c1..97c2b0b 100644
--- a/src/osgEarthSymbology/Expression.cpp
+++ b/src/osgEarthSymbology/Expression.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ExtrusionSymbol b/src/osgEarthSymbology/ExtrusionSymbol
index 6e511f3..4197995 100644
--- a/src/osgEarthSymbology/ExtrusionSymbol
+++ b/src/osgEarthSymbology/ExtrusionSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -49,6 +49,8 @@ namespace osgEarth { namespace Symbology
         };
 
     public:
+        META_Symbol(ExtrusionSymbol);
+
         ExtrusionSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -88,6 +90,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<float>             _height;
diff --git a/src/osgEarthSymbology/ExtrusionSymbol.cpp b/src/osgEarthSymbology/ExtrusionSymbol.cpp
index df337db..db23581 100644
--- a/src/osgEarthSymbology/ExtrusionSymbol.cpp
+++ b/src/osgEarthSymbology/ExtrusionSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/ExtrusionSymbol>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -60,3 +61,23 @@ ExtrusionSymbol::mergeConfig( const Config& conf )
     conf.getIfSet   ( "roof_style", _roofStyleName );
     conf.getIfSet   ( "wall_gradient", _wallGradientPercentage );
 }
+
+void
+ExtrusionSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "extrusion-height") ) {
+        style.getOrCreate<ExtrusionSymbol>()->heightExpression() = NumericExpression(c.value());
+    }
+    else if ( match(c.key(), "extrusion-flatten") ) {
+        style.getOrCreate<ExtrusionSymbol>()->flatten() = as<bool>(c.value(), true);
+    }
+    else if ( match(c.key(), "extrusion-wall-style") ) {
+        style.getOrCreate<ExtrusionSymbol>()->wallStyleName() = c.value();
+    }
+    else if ( match(c.key(), "extrusion-roof-style") ) {
+        style.getOrCreate<ExtrusionSymbol>()->roofStyleName() = c.value();
+    }
+    else if ( match(c.key(), "extrusion-wall-gradient") ) {
+        style.getOrCreate<ExtrusionSymbol>()->wallGradientPercentage() = as<float>(c.value(), 0.0f);
+    }
+}
diff --git a/src/osgEarthSymbology/SLD b/src/osgEarthSymbology/Fill
similarity index 53%
rename from src/osgEarthSymbology/SLD
rename to src/osgEarthSymbology/Fill
index 67ba253..333fe7b 100644
--- a/src/osgEarthSymbology/SLD
+++ b/src/osgEarthSymbology/Fill
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,22 +16,43 @@
  * 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 OSGEARTHSYMBOLOGY_STYLING_SLD_H
-#define OSGEARTHSYMBOLOGY_STYLING_SLD_H 1
+
+#ifndef OSGEARTHSYMBOLOGY_FILL_H
+#define OSGEARTHSYMBOLOGY_FILL_H 1
 
 #include <osgEarthSymbology/Common>
-#include <osgEarthSymbology/Style>
+#include <osgEarthSymbology/Color>
 #include <osgEarth/Config>
-#include <iostream>
+
 
 namespace osgEarth { namespace Symbology
 {
-    class OSGEARTHSYMBOLOGY_EXPORT SLDReader
+    /**
+     * Drawing parameters for a filled area.
+     */
+    class OSGEARTHSYMBOLOGY_EXPORT Fill
     {
     public:
-        static bool readStyleFromCSSParams( const Config& conf, Symbology::Style& out_style );
+        Fill();
+        Fill(const Fill& rhs);
+        Fill( float r, float g, float b, float a );
+        Fill( const Color& color );
+        Fill( const Config& conf );
+
+        virtual ~Fill() { }
+
+        Color& color() { return _color; }
+        const Color& color() const { return _color; }
+
+    public:
+        virtual Config getConfig() const;
+        virtual void mergeConfig( const Config& conf );
+
+    protected:
+        Color _color;
+        void init();
     };
 
-} } // namespace osgEarth::Features2
+} } // namespace osgEarth::Symbology
 
-#endif // OSGEARTHFEATURES_STYLING_SLD_H
+#endif // OSGEARTHSYMBOLOGY_FILL_H
diff --git a/src/osgEarthSymbology/LineSymbol.cpp b/src/osgEarthSymbology/Fill.cpp
similarity index 55%
copy from src/osgEarthSymbology/LineSymbol.cpp
copy to src/osgEarthSymbology/Fill.cpp
index 9df4128..5ef83d0 100644
--- a/src/osgEarthSymbology/LineSymbol.cpp
+++ b/src/osgEarthSymbology/Fill.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -16,32 +16,58 @@
  * 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 <osgEarthSymbology/LineSymbol>
+#include <osgEarthSymbology/Fill>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
 
-LineSymbol::LineSymbol( const Config& conf ) :
-Symbol       ( conf ),
-_stroke      ( Stroke() ),
-_tessellation( 0 )
+//------------------------------------------------------------------------
+
+Fill::Fill()
+{
+    init();
+}
+
+Fill::Fill( float r, float g, float b, float a )
+{
+    init();
+    _color.set( r, g, b, a );
+}
+
+Fill::Fill(const Color& color)
+{
+    init();
+    _color = color;
+}
+
+Fill::Fill(const Config& conf )
 {
+    init();
     mergeConfig(conf);
 }
 
-Config 
-LineSymbol::getConfig() const
+Fill::Fill(const Fill& rhs)
+{
+    init();
+    mergeConfig(rhs.getConfig());
+}
+
+void
+Fill::init()
+{
+    _color.set( 1.0f, 1.0f, 1.0f, 1.0f );
+}
+
+Config
+Fill::getConfig() const
 {
-    Config conf = Symbol::getConfig();
-    conf.key() = "line";
-    conf.addObjIfSet("stroke",       _stroke);
-    conf.addIfSet   ("tessellation", _tessellation);
+    Config conf("fill");
+    conf.add("color", _color.toHTML() );
     return conf;
 }
 
-void 
-LineSymbol::mergeConfig( const Config& conf )
+void
+Fill::mergeConfig( const Config& conf )
 {
-    conf.getObjIfSet("stroke",       _stroke);
-    conf.getIfSet   ("tessellation", _tessellation);
+    _color = Color( conf.value("color") );
 }
diff --git a/src/osgEarthSymbology/GEOS b/src/osgEarthSymbology/GEOS
index 09787c6..de52156 100644
--- a/src/osgEarthSymbology/GEOS
+++ b/src/osgEarthSymbology/GEOS
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/GEOS.cpp b/src/osgEarthSymbology/GEOS.cpp
index 5924d9d..097fdee 100644
--- a/src/osgEarthSymbology/GEOS.cpp
+++ b/src/osgEarthSymbology/GEOS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/Geometry b/src/osgEarthSymbology/Geometry
index 1a7e947..daf068f 100644
--- a/src/osgEarthSymbology/Geometry
+++ b/src/osgEarthSymbology/Geometry
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -53,8 +53,13 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT Geometry : public osgEarth::MixinVector<osg::Vec3d,osg::Referenced>
     {
     public:
+        Geometry( int capacity =0 );
+        Geometry( const Geometry& rhs );
         Geometry( const Vec3dVector* toCopy );
 
+        /** dtor - intentionally public */
+        virtual ~Geometry() { }
+
     public:
         enum Type {
             TYPE_UNKNOWN,
@@ -182,7 +187,7 @@ namespace osgEarth { namespace Symbology
         Orientation getOrientation() const;
 
     public:
-        virtual Type getType() const =0;
+        virtual Type getType() const { return TYPE_UNKNOWN; }
         virtual Type getComponentType() const { return getType(); }
         virtual bool isValid() const { return size() >= 1; }
 
@@ -195,14 +200,7 @@ namespace osgEarth { namespace Symbology
         void push_back(double x, double y, double z) { 
             osgEarth::MixinVector<osg::Vec3d,osg::Referenced>::push_back(osg::Vec3d(x,y,z)); }
 
-        /** dtor */
-        virtual ~Geometry() { }
-
     protected:
-        Geometry( int capacity =0 );
-        Geometry( const Geometry& rhs );
-
-        Vec3dVector _data;
     };
 
     typedef std::vector< osg::ref_ptr<Geometry> > GeometryCollection;
diff --git a/src/osgEarthSymbology/Geometry.cpp b/src/osgEarthSymbology/Geometry.cpp
index ef1451c..1591e24 100644
--- a/src/osgEarthSymbology/Geometry.cpp
+++ b/src/osgEarthSymbology/Geometry.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -89,6 +89,8 @@ Geometry::cloneAs( const Geometry::Type& newType ) const
             return new Polygon( *static_cast<const Polygon*>(this) );
         else
             return new Polygon( &this->asVector() );
+    case TYPE_UNKNOWN:
+        return new Geometry( &this->asVector() );
     default:
         break;
     }
diff --git a/src/osgEarthSymbology/GeometryFactory b/src/osgEarthSymbology/GeometryFactory
index 0b8a925..b51edb3 100644
--- a/src/osgEarthSymbology/GeometryFactory
+++ b/src/osgEarthSymbology/GeometryFactory
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -57,7 +57,7 @@ namespace osgEarth { namespace Symbology
             const osg::Vec3d& center,
             const Distance&   radius,
             unsigned          numSegments =0,
-            Geometry*         geomToUse   =0L);
+            Geometry*         geomToUse   =0L) const;
 
         /**
          * Creates an arc geometry.
@@ -66,6 +66,7 @@ namespace osgEarth { namespace Symbology
          * @param start  Starting angle of the arc
          * @param end    Ending angle of the arc
          * @param numSegments Number of circumference segments, or zero to automatically calculate it
+         * @param pie    Indicates to create a pie shape rather than an arc.
          */
         Geometry* createArc(
             const osg::Vec3d& center,
@@ -73,7 +74,8 @@ namespace osgEarth { namespace Symbology
             const Angle&      startAngle,
             const Angle&      endAngle,
             unsigned          numSegments =0,
-            Geometry*         geomToUse   =0L);
+            Geometry*         geomToUse   =0L,
+            bool              pie = false) const;
 
         /**
          * Creates a ellipse geometry.
@@ -90,7 +92,30 @@ namespace osgEarth { namespace Symbology
             const Distance&   radiusMinor,
             const Angle&      rotationAngle,
             unsigned          numSegments =0,
-            Geometry*         geomToUse   =0L);
+            Geometry*         geomToUse   =0L) const;
+
+        /**
+         * Creates a ellipse geometry.
+         * @param center Center point (must be in map SRS if a map was provided in ctor)
+         * @param radiusMajor Major radius (X-axis) length
+         * @param radiusMinor Minor radius (Y-axis) length
+         * @param rotationAngle with respect to the X-axis
+         * @param start  Starting angle of the arc
+         * @param end    Ending angle of the arc
+         * @param numSegments Number of circumference segments, or zero to automatically calculate it
+         * @param geomToUse Use this geometry instead of creating a Polygon.
+         * @param pie    Indicates to create a pie shape rather than an arc.
+         */
+        Geometry* createEllipticalArc(
+            const osg::Vec3d& center,
+            const Distance&   radiusMajor,
+            const Distance&   radiusMinor,
+            const Angle&      rotationAngle,
+            const Angle&      startAngle,
+            const Angle&      endAngle,
+            unsigned          numSegments =0,
+            Geometry*         geomToUse   =0L,
+            bool              pie = false) const;
 
         /**
          * Creates a rectangle geometry
@@ -101,7 +126,7 @@ namespace osgEarth { namespace Symbology
         Geometry* createRectangle(
             const osg::Vec3d& center,
             const Distance&   width,
-            const Distance&   height );
+            const Distance&   height ) const;
 
     protected:
 
diff --git a/src/osgEarthSymbology/GeometryFactory.cpp b/src/osgEarthSymbology/GeometryFactory.cpp
index fac103e..d5ed9f0 100644
--- a/src/osgEarthSymbology/GeometryFactory.cpp
+++ b/src/osgEarthSymbology/GeometryFactory.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -34,7 +34,7 @@ Geometry*
 GeometryFactory::createCircle(const osg::Vec3d& center,
                               const Distance&   radius,
                               unsigned          numSegments,
-                              Geometry*         geomToUse)
+                              Geometry*         geomToUse) const
 {
     Geometry* geom = geomToUse ? geomToUse : new Polygon();
 
@@ -87,7 +87,8 @@ GeometryFactory::createArc(const osg::Vec3d& center,
                            const Angle&      start,
                            const Angle&      end,
                            unsigned          numSegments,
-                           Geometry*         geomToUse)
+                           Geometry*         geomToUse,
+                           bool              pie) const
 {
     Geometry* geom = geomToUse? geomToUse : new LineString();
 
@@ -115,7 +116,7 @@ GeometryFactory::createArc(const osg::Vec3d& center,
         double lon = osg::DegreesToRadians(center.x());
         double rM  = radius.as(Units::METERS);
 
-        for( int i=numSegments-1; i >= 0; --i )
+        for( int i=numSegments; i >= 0; --i )
         {
             double angle = startRad + step*(double)i;
             double clat, clon;
@@ -128,7 +129,7 @@ GeometryFactory::createArc(const osg::Vec3d& center,
     {
         double rM = radius.as(Units::METERS);
 
-        for( int i=numSegments-1; i >= 0; --i )
+        for( int i=numSegments; i >= 0; --i )
         {
             double angle = startRad + step*(double)i;
             double x, y;
@@ -138,6 +139,11 @@ GeometryFactory::createArc(const osg::Vec3d& center,
         }
     }
 
+    if (pie && (geom->getTotalPointCount() > 0) && ((startRad + 2*osg::PI) != endRad))
+    {
+        geom->push_back(center);
+        geom->push_back(geom->at(0));
+    }
     geom->rewind(Geometry::ORIENTATION_CCW);
 
     return geom;
@@ -149,7 +155,7 @@ GeometryFactory::createEllipse(const osg::Vec3d& center,
                                const Distance&   radiusMinor,
                                const Angle&      rotationAngle,
                                unsigned          numSegments,
-                               Geometry*         geomToUse)
+                               Geometry*         geomToUse) const
 {
     Geometry* geom = geomToUse ? geomToUse : new Polygon();
 
@@ -210,9 +216,93 @@ GeometryFactory::createEllipse(const osg::Vec3d& center,
 }
 
 Geometry*
+GeometryFactory::createEllipticalArc(const osg::Vec3d& center,
+                                     const Distance&   radiusMajor,
+                                     const Distance&   radiusMinor,
+                                     const Angle&      rotationAngle,
+                                     const Angle&      start,
+                                     const Angle&      end,
+                                     unsigned          numSegments,
+                                     Geometry*         geomToUse,
+                                     bool              pie) const
+{
+    Geometry* geom = geomToUse ? geomToUse : new LineString();
+
+    if ( numSegments == 0 )
+    {
+        // automatically calculate
+        double ravgM = 0.5*(radiusMajor.as(Units::METERS) + radiusMinor.as(Units::METERS));
+        double segLen = ravgM / 8.0;
+        double circumference = 2*osg::PI*ravgM;
+        numSegments = (unsigned)::ceil(circumference / segLen);
+    }
+
+    double startRad = std::min( start.as(Units::RADIANS), end.as(Units::RADIANS) ) - osg::PI_2;
+    double endRad   = std::max( start.as(Units::RADIANS), end.as(Units::RADIANS) ) - osg::PI_2;
+
+    if ( endRad == startRad )
+    {
+        endRad += 2*osg::PI;
+    }
+
+    double span     = endRad - startRad;
+    double step     = span/(double)numSegments;
+
+    if ( _srs.valid() && _srs->isGeographic() )
+    {
+        double earthRadius = _srs->getEllipsoid()->getRadiusEquator();
+        double lat = osg::DegreesToRadians(center.y());
+        double lon = osg::DegreesToRadians(center.x());
+        double a = radiusMajor.as(Units::METERS);
+        double b = radiusMinor.as(Units::METERS);
+        double g = rotationAngle.as(Units::RADIANS) - osg::PI_2;
+
+        for( unsigned i=0; i<=numSegments; i++ )
+        {
+            double angle = startRad + step*(double)i;
+            double t = angle - osg::PI_2;
+            double clat, clon;
+
+            double rA = (b*b-a*a)*cos(2*t - 2*g) + a*a + b*b;
+            double q = sqrt(2.0)*a*b*sqrt(rA);
+            double r = q/rA;
+
+            GeoMath::destination( lat, lon, angle, r, clat, clon, earthRadius );
+            geom->push_back( osg::Vec3d(osg::RadiansToDegrees(clon), osg::RadiansToDegrees(clat), center.z()) );
+        }
+    }
+    else
+    {
+        double a = radiusMajor.as(Units::METERS);
+        double b = radiusMinor.as(Units::METERS);
+        double g = rotationAngle.as(Units::RADIANS) - osg::PI_2;
+        double sing = sin(g), cosg = cos(g);
+
+        for( unsigned i=0; i<=numSegments; i++ )
+        {
+            double angle = startRad + step*(double)i;
+            double t = angle - osg::PI_2;
+            double cost = cos(t), sint = sin(t);
+            double x = center.x() + a*cost*cosg - b*sint*sing;
+            double y = center.y() + a*cost*sing + b*sint*cosg;
+
+            geom->push_back( osg::Vec3d(x, y, center.z()) );
+        }
+    }
+
+    if (pie && (geom->getTotalPointCount() > 0) && ((startRad + 2*osg::PI) != endRad))
+    {
+        geom->push_back(center);
+        geom->push_back(geom->at(0));
+    }
+
+    return geom;
+}
+
+Geometry*
 GeometryFactory::createRectangle(const osg::Vec3d& center,
                                  const Distance&   width,
-                                 const Distance&   height )
+                                 const Distance&   height ) const
 {
     Geometry* geom = new Polygon();
     
diff --git a/src/osgEarthSymbology/GeometryRasterizer b/src/osgEarthSymbology/GeometryRasterizer
index 888cdfd..ba69bf8 100644
--- a/src/osgEarthSymbology/GeometryRasterizer
+++ b/src/osgEarthSymbology/GeometryRasterizer
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/GeometryRasterizer.cpp b/src/osgEarthSymbology/GeometryRasterizer.cpp
index 7ffd668..0967e17 100644
--- a/src/osgEarthSymbology/GeometryRasterizer.cpp
+++ b/src/osgEarthSymbology/GeometryRasterizer.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/IconResource b/src/osgEarthSymbology/IconResource
index dcf7047..516fe14 100644
--- a/src/osgEarthSymbology/IconResource
+++ b/src/osgEarthSymbology/IconResource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/IconResource.cpp b/src/osgEarthSymbology/IconResource.cpp
index fc40972..e1c1226 100644
--- a/src/osgEarthSymbology/IconResource.cpp
+++ b/src/osgEarthSymbology/IconResource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -114,7 +114,7 @@ IconResource::createNodeFromURI( const URI& uri, const osgDB::Options* dbOptions
 {
     osg::Node* node = 0L;
 
-    ReadResult r = uri.readObject( dbOptions );
+    ReadResult r = uri.readImage( dbOptions );
     if ( r.succeeded() )
     {
         if ( r.getImage() )
diff --git a/src/osgEarthSymbology/IconSymbol b/src/osgEarthSymbology/IconSymbol
index 66a1ac9..b58cf18 100644
--- a/src/osgEarthSymbology/IconSymbol
+++ b/src/osgEarthSymbology/IconSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
 
 namespace osgEarth { namespace Symbology
 {
+    class Style;
     class InstanceResource;
 
     /**
@@ -49,6 +50,8 @@ namespace osgEarth { namespace Symbology
         };
 
     public:
+        META_Symbol(IconSymbol);
+
         IconSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -61,6 +64,10 @@ namespace osgEarth { namespace Symbology
         /** Heading. Semantically this differs from an model's heading */
         optional<NumericExpression>& heading() { return _heading; }
         const optional<NumericExpression>& heading() const { return _heading; }
+
+        /** Decluttering */
+        optional<bool>& declutter() { return _declutter; }
+        const optional<bool>& declutter() const { return _declutter; }
         
     public: // non-serialized properties (for programmatic use only)
 
@@ -71,6 +78,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     public: // InstanceSymbol
         virtual InstanceResource* createResource() const;
@@ -78,6 +86,7 @@ namespace osgEarth { namespace Symbology
     protected:
         optional<Alignment>              _alignment;
         optional<NumericExpression>      _heading;
+        optional<bool>                   _declutter;
         mutable osg::ref_ptr<osg::Image> _image;
     };
 
diff --git a/src/osgEarthSymbology/IconSymbol.cpp b/src/osgEarthSymbology/IconSymbol.cpp
index 144ce8a..e31f9d4 100644
--- a/src/osgEarthSymbology/IconSymbol.cpp
+++ b/src/osgEarthSymbology/IconSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
  */
 #include <osgEarthSymbology/IconSymbol>
 #include <osgEarthSymbology/IconResource>
+#include <osgEarthSymbology/Style>
 #include <osgEarth/ThreadingUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/ImageUtils>
@@ -29,7 +30,8 @@ using namespace osgEarth::Symbology;
 IconSymbol::IconSymbol( const Config& conf ) :
 InstanceSymbol( conf ),
 _alignment    ( ALIGN_CENTER_BOTTOM ),
-_heading      ( NumericExpression(0.0) )
+_heading      ( NumericExpression(0.0) ),
+_declutter    ( false )
 {
     mergeConfig( conf );
 }
@@ -50,7 +52,8 @@ IconSymbol::getConfig() const
     conf.addIfSet( "alignment", "right_center",  _alignment, ALIGN_RIGHT_CENTER );
     conf.addIfSet( "alignment", "right_bottom",  _alignment, ALIGN_RIGHT_BOTTOM );
 
-    conf.addObjIfSet( "heading", _heading );
+    conf.addObjIfSet( "heading",   _heading );
+    conf.addIfSet   ( "declutter", _declutter );
 
     conf.addNonSerializable( "IconSymbol::image", _image.get() );
     return conf;
@@ -69,7 +72,8 @@ IconSymbol::mergeConfig( const Config& conf )
     conf.getIfSet( "alignment", "right_center",  _alignment, ALIGN_RIGHT_CENTER );
     conf.getIfSet( "alignment", "right_bottom",  _alignment, ALIGN_RIGHT_BOTTOM );
 
-    conf.getObjIfSet( "heading", _heading );
+    conf.getObjIfSet( "heading",   _heading );
+    conf.getIfSet   ( "declutter", _declutter );
 
     _image = conf.getNonSerializable<osg::Image>( "IconSymbol::image" );
 }
@@ -119,3 +123,61 @@ IconSymbol::createResource() const
 {
     return new IconResource();
 }
+
+
+void
+IconSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "icon") ) {
+        style.getOrCreate<IconSymbol>()->url() = c.value();
+        style.getOrCreate<IconSymbol>()->url()->setURIContext( c.referrer() );
+    }
+    else if ( match(c.key(),"icon-library") ) {
+        style.getOrCreate<IconSymbol>()->libraryName() = StringExpression(c.value());
+    }
+    else if ( match(c.key(), "icon-placement") ) {
+        if      ( match(c.value(), "vertex") )   
+            style.getOrCreate<IconSymbol>()->placement() = ModelSymbol::PLACEMENT_VERTEX;
+        else if ( match(c.value(), "interval") ) 
+            style.getOrCreate<IconSymbol>()->placement() = ModelSymbol::PLACEMENT_INTERVAL;
+        else if ( match(c.value(), "random") )   
+            style.getOrCreate<IconSymbol>()->placement() = ModelSymbol::PLACEMENT_RANDOM;
+        else if ( match(c.value(), "centroid") ) 
+            style.getOrCreate<IconSymbol>()->placement() = ModelSymbol::PLACEMENT_CENTROID;
+    }
+    else if ( match(c.key(), "icon-density") ) {
+        style.getOrCreate<IconSymbol>()->density() = as<float>(c.value(), 1.0f);
+    }
+    else if ( match(c.key(), "icon-random-seed") ) {
+        style.getOrCreate<IconSymbol>()->randomSeed() = as<unsigned>(c.value(), 0);
+    }
+    else if ( match(c.key(), "icon-scale") ) {
+        style.getOrCreate<IconSymbol>()->scale() = NumericExpression(c.value());
+    }
+    else if ( match(c.key(), "icon-align") ) {
+        if      ( match(c.value(), "left-top") )      
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_LEFT_TOP;
+        else if ( match(c.value(), "left-center") )   
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_LEFT_CENTER;
+        else if ( match(c.value(), "left-bottom") )   
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_LEFT_BOTTOM;
+        else if ( match(c.value(), "center-top")  )   
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_CENTER_TOP;
+        else if ( match(c.value(), "center-center") ) 
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_CENTER_CENTER;
+        else if ( match(c.value(), "center-bottom") ) 
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_CENTER_BOTTOM;
+        else if ( match(c.value(), "right-top") )     
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_RIGHT_TOP;
+        else if ( match(c.value(), "right-center") )  
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_RIGHT_CENTER;
+        else if ( match(c.value(), "right-bottom") )  
+            style.getOrCreate<IconSymbol>()->alignment() = IconSymbol::ALIGN_RIGHT_BOTTOM;
+    }
+    else if ( match(c.key(), "icon-heading") ) {
+        style.getOrCreate<IconSymbol>()->heading() = NumericExpression(c.value());
+    }
+    else if ( match(c.key(), "icon-declutter") ) {
+        style.getOrCreate<IconSymbol>()->declutter() = as<bool>(c.value(), false);
+    }
+}
diff --git a/src/osgEarthSymbology/InstanceResource b/src/osgEarthSymbology/InstanceResource
index 7d8b08c..97c226d 100644
--- a/src/osgEarthSymbology/InstanceResource
+++ b/src/osgEarthSymbology/InstanceResource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/InstanceResource.cpp b/src/osgEarthSymbology/InstanceResource.cpp
index bb451ee..2cca5fb 100644
--- a/src/osgEarthSymbology/InstanceResource.cpp
+++ b/src/osgEarthSymbology/InstanceResource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/InstanceSymbol b/src/osgEarthSymbology/InstanceSymbol
index d89ded3..641095b 100644
--- a/src/osgEarthSymbology/InstanceSymbol
+++ b/src/osgEarthSymbology/InstanceSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/InstanceSymbol.cpp b/src/osgEarthSymbology/InstanceSymbol.cpp
index 819025e..ef21cc0 100644
--- a/src/osgEarthSymbology/InstanceSymbol.cpp
+++ b/src/osgEarthSymbology/InstanceSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/LineSymbol b/src/osgEarthSymbology/LineSymbol
index 5f592dd..2d44b39 100644
--- a/src/osgEarthSymbology/LineSymbol
+++ b/src/osgEarthSymbology/LineSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,12 +20,8 @@
 #ifndef OSGEARTHSYMBOLOGY_LINE_SYMBOL_H
 #define OSGEARTHSYMBOLOGY_LINE_SYMBOL_H 1
 
-#include <osgEarth/Common>
-#include <osgEarth/GeoMath>
-#include <osgEarthSymbology/Common>
 #include <osgEarthSymbology/Symbol>
-#include <osg/Referenced>
-#include <vector>
+#include <osgEarthSymbology/Stroke>
 
 namespace osgEarth { namespace Symbology
 {
@@ -35,6 +31,8 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT LineSymbol : public Symbol
     {
     public:
+        META_Symbol(LineSymbol);
+
         LineSymbol(const Config& conf =Config());
 
         /** dtor */
@@ -51,6 +49,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<Stroke>   _stroke;
diff --git a/src/osgEarthSymbology/LineSymbol.cpp b/src/osgEarthSymbology/LineSymbol.cpp
index 9df4128..785057b 100644
--- a/src/osgEarthSymbology/LineSymbol.cpp
+++ b/src/osgEarthSymbology/LineSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/LineSymbol>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -45,3 +46,44 @@ LineSymbol::mergeConfig( const Config& conf )
     conf.getObjIfSet("stroke",       _stroke);
     conf.getIfSet   ("tessellation", _tessellation);
 }
+
+void
+LineSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "stroke") ) {
+        style.getOrCreate<LineSymbol>()->stroke()->color() = Color(c.value());
+    }
+    else if ( match(c.key(), "stroke-opacity") ) {
+        style.getOrCreate<LineSymbol>()->stroke()->color().a() = as<float>( c.value(), 1.0f );
+    }
+    else if ( match(c.key(), "stroke-width") ) {
+        float width;
+        Units units;
+        if ( Units::parse(c.value(), width, units, Units::PIXELS) )
+        {
+            style.getOrCreate<LineSymbol>()->stroke()->width() = width;
+            style.getOrCreate<LineSymbol>()->stroke()->widthUnits() = units;
+        }
+    }
+    else if ( match(c.key(), "stroke-linecap") ) {
+        style.getOrCreate<LineSymbol>()->stroke()->lineCap() =
+            c.value() == "flat"   ?   Stroke::LINECAP_FLAT   :
+            c.value() == "square" ?   Stroke::LINECAP_SQUARE :
+            /*value == "round"   ?*/  Stroke::LINECAP_ROUND;
+    }
+    else if (match(c.key(), "stroke-linejoin") ) {
+        style.getOrCreate<LineSymbol>()->stroke()->lineJoin() =
+            c.value() == "mitre" ?      Stroke::LINEJOIN_MITRE :
+            c.value() == "miter" ?      Stroke::LINEJOIN_MITRE : // alternate spelling
+            /*c.value() == "round"  ?*/ Stroke::LINEJOIN_ROUND;
+    }
+    else if ( match(c.key(), "stroke-rounding-ratio") ) {
+        style.getOrCreate<LineSymbol>()->stroke()->roundingRatio() = as<float>(c.value(), 0.4f);
+    }
+    else if ( match(c.key(), "stroke-tessellation") ) {
+        style.getOrCreate<LineSymbol>()->tessellation() = as<unsigned>( c.value(), 0 );
+    }
+    else if ( match(c.key(), "stroke-min-pixels") ) {
+        style.getOrCreate<LineSymbol>()->stroke()->minPixels() = as<float>(c.value(), 0.0f);
+    }
+}
diff --git a/src/osgEarthSymbology/MarkerResource b/src/osgEarthSymbology/MarkerResource
index 2480b14..4122c13 100644
--- a/src/osgEarthSymbology/MarkerResource
+++ b/src/osgEarthSymbology/MarkerResource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/MarkerResource.cpp b/src/osgEarthSymbology/MarkerResource.cpp
index e50e9db..90f17c1 100644
--- a/src/osgEarthSymbology/MarkerResource.cpp
+++ b/src/osgEarthSymbology/MarkerResource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/MarkerSymbol b/src/osgEarthSymbology/MarkerSymbol
index 097edd4..ed1a9da 100644
--- a/src/osgEarthSymbology/MarkerSymbol
+++ b/src/osgEarthSymbology/MarkerSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -73,6 +73,8 @@ namespace osgEarth { namespace Symbology
         };
 
     public:
+        META_Symbol(MarkerSymbol);
+
         MarkerSymbol( const Config& conf =Config() );
 
         /** Since MarkerSymbol is deprecated, this conveneince function will convert to an InstanceSymbol */
@@ -130,6 +132,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<StringExpression>   _url;
diff --git a/src/osgEarthSymbology/MarkerSymbol.cpp b/src/osgEarthSymbology/MarkerSymbol.cpp
index 8805ba9..28a5bf7 100644
--- a/src/osgEarthSymbology/MarkerSymbol.cpp
+++ b/src/osgEarthSymbology/MarkerSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
 #include <osgEarthSymbology/MarkerSymbol>
 #include <osgEarthSymbology/IconSymbol>
 #include <osgEarthSymbology/ModelSymbol>
+#include <osgEarthSymbology/Style>
 #include <osgEarth/ThreadingUtils>
 #include <osgEarth/Registry>
 #include <osgEarth/ImageUtils>
@@ -223,3 +224,55 @@ MarkerSymbol::convertToInstanceSymbol() const
 
     return result;
 }
+
+
+void
+MarkerSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "marker") ) {
+        style.getOrCreate<MarkerSymbol>()->url() = c.value();
+        style.getOrCreate<MarkerSymbol>()->url()->setURIContext( c.referrer() );
+    }
+    else if ( match(c.key(),"marker-library") ) {
+        style.getOrCreate<MarkerSymbol>()->libraryName() = StringExpression(c.value());
+    }
+    else if ( match(c.key(), "marker-placement") ) {
+        if      ( match(c.value(), "vertex") )   
+            style.getOrCreate<MarkerSymbol>()->placement() = MarkerSymbol::PLACEMENT_VERTEX;
+        else if ( match(c.value(), "interval") ) 
+            style.getOrCreate<MarkerSymbol>()->placement() = MarkerSymbol::PLACEMENT_INTERVAL;
+        else if ( match(c.value(), "random") )   
+            style.getOrCreate<MarkerSymbol>()->placement() = MarkerSymbol::PLACEMENT_RANDOM;
+        else if ( match(c.value(), "centroid") ) 
+            style.getOrCreate<MarkerSymbol>()->placement() = MarkerSymbol::PLACEMENT_CENTROID;
+    }
+    else if ( match(c.key(), "marker-density") ) {
+        style.getOrCreate<MarkerSymbol>()->density() = as<float>(c.value(), 1.0f);
+    }
+    else if ( match(c.key(), "marker-random-seed") ) {
+        style.getOrCreate<MarkerSymbol>()->randomSeed() = as<unsigned>(c.value(), 0);
+    }
+    else if ( match(c.key(), "marker-scale") ) {
+        style.getOrCreate<MarkerSymbol>()->scale() = NumericExpression(c.value());
+    }
+    else if ( match(c.key(), "marker-icon-align") ) {
+        if      ( match(c.value(), "left-top") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_LEFT_TOP;
+        else if ( match(c.value(), "left-center") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_LEFT_CENTER;
+        else if ( match(c.value(), "left-bottom") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_LEFT_BOTTOM;
+        else if ( match(c.value(), "center-top")  ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_CENTER_TOP;
+        else if ( match(c.value(), "center-center") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_CENTER_CENTER;
+        else if ( match(c.value(), "center-bottom") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_CENTER_BOTTOM;
+        else if ( match(c.value(), "right-top") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_RIGHT_TOP;
+        else if ( match(c.value(), "right-center") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_RIGHT_CENTER;
+        else if ( match(c.value(), "right-bottom") ) 
+            style.getOrCreate<MarkerSymbol>()->alignment() = MarkerSymbol::ALIGN_RIGHT_BOTTOM;
+    }
+}
\ No newline at end of file
diff --git a/src/osgEarthSymbology/MeshConsolidator b/src/osgEarthSymbology/MeshConsolidator
index c53c123..01c551b 100644
--- a/src/osgEarthSymbology/MeshConsolidator
+++ b/src/osgEarthSymbology/MeshConsolidator
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/MeshConsolidator.cpp b/src/osgEarthSymbology/MeshConsolidator.cpp
index b0af962..6398db8 100644
--- a/src/osgEarthSymbology/MeshConsolidator.cpp
+++ b/src/osgEarthSymbology/MeshConsolidator.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -315,23 +315,23 @@ namespace
     {
         osg::Geometry::AttributeBinding newColorsBinding, newNormalsBinding;
 
-	    osg::Vec3Array* newVerts = new osg::Vec3Array();
-	    newVerts->reserve( numVerts );
+        osg::Vec3Array* newVerts = new osg::Vec3Array();
+        newVerts->reserve( numVerts );
 
-	    osg::Vec4Array* newColors =0L;
-	    if ( numColors > 0 )
-	    {
-		    newColors = new osg::Vec4Array();
+        osg::Vec4Array* newColors =0L;
+        if ( numColors > 0 )
+        {
+            newColors = new osg::Vec4Array();
             newColors->reserve( numVerts );
             newColorsBinding = osg::Geometry::BIND_PER_VERTEX;
             //newColors->reserve( numColors==numVerts? numColors : 1 );
             //newColorsBinding = numColors==numVerts? osg::Geometry::BIND_PER_VERTEX : osg::Geometry::BIND_OVERALL;
-	    }
+        }
 
-	    osg::Vec3Array* newNormals =0L;
-	    if ( numNormals > 0 )
-	    {
-		    newNormals = new osg::Vec3Array();
+        osg::Vec3Array* newNormals =0L;
+        if ( numNormals > 0 )
+        {
+            newNormals = new osg::Vec3Array();
             newNormals->reserve( numVerts );
             newNormalsBinding = osg::Geometry::BIND_PER_VERTEX;
             //newNormals->reserve( numNormals==numVerts? numNormals : 1 );
@@ -344,18 +344,18 @@ namespace
             osg::Vec2Array* newTexCoords = new osg::Vec2Array();
             newTexCoords->reserve( numVerts );
             newTexCoordsArrays.push_back( newTexCoords );
-	    }
+        }
 
-	    unsigned offset = 0;
-	    osg::Geometry::PrimitiveSetList newPrimSets;
+        unsigned offset = 0;
+        osg::Geometry::PrimitiveSetList newPrimSets;
 
         std::vector<osg::ref_ptr<osg::Geometry> > nonOptimizedGeoms;
 
         osg::StateSet* unifiedStateSet = 0L;
 
         for( DrawableList::iterator i = start; i != end; ++i )
-	    {
-		    osg::Geometry* geom = i->get()->asGeometry(); //geode.getDrawable(i)->asGeometry();
+        {
+            osg::Geometry* geom = i->get()->asGeometry(); //geode.getDrawable(i)->asGeometry();
 
             // merge in the stateset:
             if ( unifiedStateSet == 0L )
@@ -363,43 +363,43 @@ namespace
             else if ( geom->getStateSet() )
                 unifiedStateSet->merge( *geom->getStateSet() );            
 
-		    // copy over the verts:
-		    osg::Vec3Array* geomVerts = dynamic_cast<osg::Vec3Array*>( geom->getVertexArray() );
-		    if ( geomVerts )
-		    {
-			    std::copy( geomVerts->begin(), geomVerts->end(), std::back_inserter(*newVerts) );
-
-			    if ( newColors )
-			    {
-				    osg::Vec4Array* colors = dynamic_cast<osg::Vec4Array*>( geom->getColorArray() );
-				    if ( colors )
-				    {
-					    if ( newColorsBinding == osg::Geometry::BIND_PER_VERTEX )
-					    {
-						    std::copy( colors->begin(), colors->end(), std::back_inserter(*newColors) );
-					    }
-					    else if ( i == start ) //i == 0 ) // overall
-					    {
-						    newColors->push_back( (*colors)[0] );
-					    }
-				    }
-			    }
-
-			    if ( newNormals )
-			    {
-				    osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>( geom->getNormalArray() );
-				    if ( normals )
-				    {
-					    if ( newNormalsBinding == osg::Geometry::BIND_PER_VERTEX )
-					    {
-						    std::copy( normals->begin(), normals->end(), std::back_inserter(*newNormals) );
-					    }
-					    else if ( i == start ) //0 ) // overall
-					    {
-						    newNormals->push_back( (*normals)[0] );
-					    }
-				    }
-			    }
+            // copy over the verts:
+            osg::Vec3Array* geomVerts = dynamic_cast<osg::Vec3Array*>( geom->getVertexArray() );
+            if ( geomVerts )
+            {
+                std::copy( geomVerts->begin(), geomVerts->end(), std::back_inserter(*newVerts) );
+
+                if ( newColors )
+                {
+                    osg::Vec4Array* colors = dynamic_cast<osg::Vec4Array*>( geom->getColorArray() );
+                    if ( colors )
+                    {
+                        if ( newColorsBinding == osg::Geometry::BIND_PER_VERTEX )
+                        {
+                            std::copy( colors->begin(), colors->end(), std::back_inserter(*newColors) );
+                        }
+                        else if ( i == start ) //i == 0 ) // overall
+                        {
+                            newColors->push_back( (*colors)[0] );
+                        }
+                    }
+                }
+
+                if ( newNormals )
+                {
+                    osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>( geom->getNormalArray() );
+                    if ( normals )
+                    {
+                        if ( newNormalsBinding == osg::Geometry::BIND_PER_VERTEX )
+                        {
+                            std::copy( normals->begin(), normals->end(), std::back_inserter(*newNormals) );
+                        }
+                        else if ( i == start ) //0 ) // overall
+                        {
+                            newNormals->push_back( (*normals)[0] );
+                        }
+                    }
+                }
 
                 if ( newTexCoordsArrays.size() > 0 )
                 {
@@ -417,53 +417,53 @@ namespace
 
                 osg::ref_ptr<osg::Referenced> sharedUserData;
 
-			    for( unsigned j=0; j < geom->getNumPrimitiveSets(); ++j )
-			    {
-				    osg::PrimitiveSet* pset = geom->getPrimitiveSet(j);
-				    osg::PrimitiveSet* newpset = 0L;
+                for( unsigned j=0; j < geom->getNumPrimitiveSets(); ++j )
+                {
+                    osg::PrimitiveSet* pset = geom->getPrimitiveSet(j);
+                    osg::PrimitiveSet* newpset = 0L;
 
                     // all primsets have the same user data (or else we would not have made it this far
                     // since canOptimize would be false)
                     if ( !sharedUserData.valid() )
                         sharedUserData = pset->getUserData();
 
-				    if ( dynamic_cast<osg::DrawElementsUByte*>(pset) )
-					    newpset = remake( static_cast<osg::DrawElementsUByte*>(pset), numVerts, offset );
-				    else if ( dynamic_cast<osg::DrawElementsUShort*>(pset) )
-					    newpset = remake( static_cast<osg::DrawElementsUShort*>(pset), numVerts, offset );
-				    else if ( dynamic_cast<osg::DrawElementsUInt*>(pset) )
-					    newpset = remake( static_cast<osg::DrawElementsUInt*>(pset), numVerts, offset );
-				    else if ( dynamic_cast<osg::DrawArrays*>(pset) )
+                    if ( dynamic_cast<osg::DrawElementsUByte*>(pset) )
+                        newpset = remake( static_cast<osg::DrawElementsUByte*>(pset), numVerts, offset );
+                    else if ( dynamic_cast<osg::DrawElementsUShort*>(pset) )
+                        newpset = remake( static_cast<osg::DrawElementsUShort*>(pset), numVerts, offset );
+                    else if ( dynamic_cast<osg::DrawElementsUInt*>(pset) )
+                        newpset = remake( static_cast<osg::DrawElementsUInt*>(pset), numVerts, offset );
+                    else if ( dynamic_cast<osg::DrawArrays*>(pset) )
                         newpset = convertDAtoDE( static_cast<osg::DrawArrays*>(pset), numVerts, offset );
 
-				    if ( newpset )
-				    {
+                    if ( newpset )
+                    {
                         newpset->setUserData( sharedUserData.get() );
 
-					    newPrimSets.push_back( newpset );
-				    }
-			    }
+                        newPrimSets.push_back( newpset );
+                    }
+                }
 
-			    offset += geomVerts->size();
-		    }
-	    }
+                offset += geomVerts->size();
+            }
+        }
 
-	    // assemble the new geometry.
-	    osg::Geometry* newGeom = new osg::Geometry();
+        // assemble the new geometry.
+        osg::Geometry* newGeom = new osg::Geometry();
 
-	    newGeom->setVertexArray( newVerts );
+        newGeom->setVertexArray( newVerts );
 
-	    if ( newColors )
-	    {
-		    newGeom->setColorArray( newColors );
-		    newGeom->setColorBinding( newColorsBinding );
-	    }
+        if ( newColors )
+        {
+            newGeom->setColorArray( newColors );
+            newGeom->setColorBinding( newColorsBinding );
+        }
 
-	    if ( newNormals )
-	    {
-		    newGeom->setNormalArray( newNormals );
-		    newGeom->setNormalBinding( newNormalsBinding );
-	    }
+        if ( newNormals )
+        {
+            newGeom->setNormalArray( newNormals );
+            newGeom->setNormalBinding( newNormalsBinding );
+        }
 
         if ( newTexCoordsArrays.size() > 0 )
         {
@@ -474,7 +474,7 @@ namespace
             }
         }
 
-	    newGeom->setPrimitiveSetList( newPrimSets );
+        newGeom->setPrimitiveSetList( newPrimSets );
         newGeom->setStateSet( unifiedStateSet );
 
         newGeom->setUseVertexBufferObjects( useVBOs );
@@ -573,7 +573,7 @@ MeshConsolidator::run( osg::Geode& geode )
     }
 
     // re-build the geode:
-	geode.removeDrawables( 0, geode.getNumDrawables() );
+    geode.removeDrawables( 0, geode.getNumDrawables() );
 
     for( DrawableList::iterator i = results.begin(); i != results.end(); ++i )
         geode.addDrawable( i->get() );
diff --git a/src/osgEarthSymbology/MeshSubdivider b/src/osgEarthSymbology/MeshSubdivider
index 89d4e01..c0c971b 100644
--- a/src/osgEarthSymbology/MeshSubdivider
+++ b/src/osgEarthSymbology/MeshSubdivider
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/MeshSubdivider.cpp b/src/osgEarthSymbology/MeshSubdivider.cpp
index 711e01b..83508c3 100644
--- a/src/osgEarthSymbology/MeshSubdivider.cpp
+++ b/src/osgEarthSymbology/MeshSubdivider.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ModelResource b/src/osgEarthSymbology/ModelResource
index f1e4bf0..63cbc16 100644
--- a/src/osgEarthSymbology/ModelResource
+++ b/src/osgEarthSymbology/ModelResource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ModelResource.cpp b/src/osgEarthSymbology/ModelResource.cpp
index de560b1..eced65c 100644
--- a/src/osgEarthSymbology/ModelResource.cpp
+++ b/src/osgEarthSymbology/ModelResource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ModelSymbol b/src/osgEarthSymbology/ModelSymbol
index ccbc91d..8207d86 100644
--- a/src/osgEarthSymbology/ModelSymbol
+++ b/src/osgEarthSymbology/ModelSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -36,6 +36,8 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT ModelSymbol : public InstanceSymbol
     {
     public:
+        META_Symbol(ModelSymbol);
+
         ModelSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -62,6 +64,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
     
     public: // InstanceSymbol
         /** Creates a new (empty) resource appropriate for this symbol */
diff --git a/src/osgEarthSymbology/ModelSymbol.cpp b/src/osgEarthSymbology/ModelSymbol.cpp
index b47394e..73cf271 100644
--- a/src/osgEarthSymbology/ModelSymbol.cpp
+++ b/src/osgEarthSymbology/ModelSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
  */
 #include <osgEarthSymbology/ModelSymbol>
 #include <osgEarthSymbology/ModelResource>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -63,3 +64,35 @@ ModelSymbol::createResource() const
 {
     return new ModelResource();
 }
+
+void
+ModelSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "model") ) {
+        style.getOrCreate<ModelSymbol>()->url() = c.value();
+        style.getOrCreate<ModelSymbol>()->url()->setURIContext( c.referrer() );
+    }
+    else if ( match(c.key(), "model-placement") ) {
+        if      ( match(c.value(), "vertex") )   
+            style.getOrCreate<ModelSymbol>()->placement() = ModelSymbol::PLACEMENT_VERTEX;
+        else if ( match(c.value(), "interval") ) 
+            style.getOrCreate<ModelSymbol>()->placement() = ModelSymbol::PLACEMENT_INTERVAL;
+        else if ( match(c.value(), "random") )   
+            style.getOrCreate<ModelSymbol>()->placement() = ModelSymbol::PLACEMENT_RANDOM;
+        else if ( match(c.value(), "centroid") ) 
+            style.getOrCreate<ModelSymbol>()->placement() = ModelSymbol::PLACEMENT_CENTROID;
+    }
+    else if ( match(c.key(), "model-density") ) {
+        style.getOrCreate<ModelSymbol>()->density() = as<float>(c.value(), 1.0f);
+    }
+    else if ( match(c.key(), "model-random-seed") ) {
+        style.getOrCreate<ModelSymbol>()->randomSeed() = as<unsigned>(c.value(), 0);
+    }
+    else if ( match(c.key(), "model-scale") ) {
+        style.getOrCreate<ModelSymbol>()->scale() = NumericExpression(c.value());
+    }
+    else if ( match(c.key(), "model-heading") ) {
+        style.getOrCreate<ModelSymbol>()->heading() = NumericExpression(c.value());
+    }
+
+}
\ No newline at end of file
diff --git a/src/osgEarthSymbology/PointSymbol b/src/osgEarthSymbology/PointSymbol
index 199831c..2cc067b 100644
--- a/src/osgEarthSymbology/PointSymbol
+++ b/src/osgEarthSymbology/PointSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -19,8 +19,8 @@
 #ifndef OSGEARTHSYMBOLOGY_POINT_SYMBOL_H
 #define OSGEARTHSYMBOLOGY_POINT_SYMBOL_H 1
 
-#include <osgEarthSymbology/Common>
 #include <osgEarthSymbology/Symbol>
+#include <osgEarthSymbology/Fill>
 
 namespace osgEarth { namespace Symbology
 {
@@ -30,6 +30,8 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT PointSymbol : public Symbol
     {
     public:
+        META_Symbol(PointSymbol);
+
         PointSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -46,6 +48,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<Fill>  _fill;
diff --git a/src/osgEarthSymbology/PointSymbol.cpp b/src/osgEarthSymbology/PointSymbol.cpp
index 848c7d0..3f6ae54 100644
--- a/src/osgEarthSymbology/PointSymbol.cpp
+++ b/src/osgEarthSymbology/PointSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/PointSymbol>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -45,3 +46,18 @@ PointSymbol::mergeConfig( const Config& conf )
     conf.getObjIfSet( "fill", _fill );
     conf.getIfSet( "size", _size );
 }
+
+
+void
+PointSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "fill") ) {
+        style.getOrCreate<PointSymbol>()->fill()->color() = Color(c.value());
+    }
+    else if ( match(c.key(), "fill-opacity") ) {
+        style.getOrCreate<PointSymbol>()->fill()->color().a() = as<float>( c.value(), 1.0f );
+    }
+    else if ( match(c.key(), "point-size") ) {
+        style.getOrCreate<PointSymbol>()->size() = as<float>(c.value(), 1.0f);
+    }
+}
\ No newline at end of file
diff --git a/src/osgEarthSymbology/PolygonSymbol b/src/osgEarthSymbology/PolygonSymbol
index 8c4b80e..a0e4932 100644
--- a/src/osgEarthSymbology/PolygonSymbol
+++ b/src/osgEarthSymbology/PolygonSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -20,11 +20,8 @@
 #ifndef OSGEARTHSYMBOLOGY_GEOMETRY_SYMBOL_H
 #define OSGEARTHSYMBOLOGY_GEOMETRY_SYMBOL_H 1
 
-#include <osgEarth/Common>
-#include <osgEarthSymbology/Common>
 #include <osgEarthSymbology/Symbol>
-#include <osg/Referenced>
-#include <vector>
+#include <osgEarthSymbology/Fill>
 
 namespace osgEarth { namespace Symbology
 {
@@ -34,6 +31,8 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT PolygonSymbol : public Symbol
     {
     public:
+        META_Symbol(PolygonSymbol);
+
         PolygonSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -46,6 +45,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig(const Config& conf);
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<Fill> _fill;
diff --git a/src/osgEarthSymbology/PolygonSymbol.cpp b/src/osgEarthSymbology/PolygonSymbol.cpp
index 53c2f56..76fc43c 100644
--- a/src/osgEarthSymbology/PolygonSymbol.cpp
+++ b/src/osgEarthSymbology/PolygonSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/PolygonSymbol>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -42,3 +43,14 @@ PolygonSymbol::mergeConfig(const Config& conf )
 {
     conf.getObjIfSet( "fill", _fill );
 }
+
+void
+PolygonSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "fill") ) {
+        style.getOrCreate<PolygonSymbol>()->fill()->color() = Color(c.value());
+    }
+    else if ( match(c.key(), "fill-opacity") ) {
+        style.getOrCreate<PolygonSymbol>()->fill()->color().a() = as<float>( c.value(), 1.0f );
+    }
+}
diff --git a/src/osgEarthSymbology/Query b/src/osgEarthSymbology/Query
index 0e8ae70..d314c76 100644
--- a/src/osgEarthSymbology/Query
+++ b/src/osgEarthSymbology/Query
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/Query.cpp b/src/osgEarthSymbology/Query.cpp
index d96c536..ed97472 100644
--- a/src/osgEarthSymbology/Query.cpp
+++ b/src/osgEarthSymbology/Query.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/RenderSymbol b/src/osgEarthSymbology/RenderSymbol
new file mode 100644
index 0000000..cb10f14
--- /dev/null
+++ b/src/osgEarthSymbology/RenderSymbol
@@ -0,0 +1,72 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 OSGEARTHSYMBOLOGY_RENDER_SYMBOL_H
+#define OSGEARTHSYMBOLOGY_RENDER_SYMBOL_H 1
+
+#include <osgEarth/Common>
+#include <osgEarth/DepthOffset>
+#include <osgEarth/GeoMath>
+#include <osgEarthSymbology/Common>
+#include <osgEarthSymbology/Symbol>
+#include <osg/Referenced>
+#include <vector>
+
+namespace osgEarth { namespace Symbology
+{
+    /** 
+     * Symbol that contains general rendering settings.
+     */
+    class OSGEARTHSYMBOLOGY_EXPORT RenderSymbol : public Symbol
+    {
+    public:
+        META_Symbol(RenderSymbol);
+
+        /** construct a render symbol */
+        RenderSymbol(const Config& conf =Config());
+
+        /** whether to perform depth buffer testing */
+        optional<bool>& depthTest() { return _depthTest; }
+        const optional<bool>& depthTest() const { return _depthTest; }
+
+        /** whether to force lighting on/off */
+        optional<bool>& lighting() { return _lighting; }
+        const optional<bool>& lighting() const { return _lighting; }
+
+        /** depth offset properties */
+        optional<DepthOffsetOptions>& depthOffset() { return _depthOffset; }
+        const optional<DepthOffsetOptions>& depthOffset() const { return _depthOffset; }
+
+    public:
+        virtual Config getConfig() const;
+        virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
+
+    protected:
+        optional<bool>               _depthTest;
+        optional<bool>               _lighting;
+        optional<DepthOffsetOptions> _depthOffset;
+        
+        /** dtor */
+        virtual ~RenderSymbol() { }
+    };
+
+} } // namespace osgEarth::Symbology
+
+#endif // OSGEARTHSYMBOLOGY_RENDER_SYMBOL_H
diff --git a/src/osgEarthSymbology/RenderSymbol.cpp b/src/osgEarthSymbology/RenderSymbol.cpp
new file mode 100644
index 0000000..4b52f53
--- /dev/null
+++ b/src/osgEarthSymbology/RenderSymbol.cpp
@@ -0,0 +1,76 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 <osgEarthSymbology/RenderSymbol>
+#include <osgEarthSymbology/Style>
+
+using namespace osgEarth;
+using namespace osgEarth::Symbology;
+
+RenderSymbol::RenderSymbol(const Config& conf) :
+Symbol    ( conf ),
+_depthTest( true ),
+_lighting ( true )
+{
+    mergeConfig(conf);
+}
+
+Config 
+RenderSymbol::getConfig() const
+{
+    Config conf = Symbol::getConfig();
+    conf.key() = "render";
+    conf.addIfSet   ( "depth_test",   _depthTest );
+    conf.addIfSet   ( "lighting",     _lighting );
+    conf.addObjIfSet( "depth_offset", _depthOffset );
+    return conf;
+}
+
+void 
+RenderSymbol::mergeConfig( const Config& conf )
+{
+    conf.getIfSet   ( "depth_test",   _depthTest );
+    conf.getIfSet   ( "lighting",     _lighting );
+    conf.getObjIfSet( "depth_offset", _depthOffset );
+}
+
+void
+RenderSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "render-depth-test") ) {
+        style.getOrCreate<RenderSymbol>()->depthTest() = as<bool>(c.value(), true);
+    }
+    else if ( match(c.key(), "render-lighting") ) {
+        style.getOrCreate<RenderSymbol>()->lighting() = as<bool>(c.value(), false);
+    }
+    else if ( match(c.key(), "render-depth-offset") ) {
+        style.getOrCreate<RenderSymbol>()->depthOffset()->enabled() = as<bool>(c.value(), false);
+    }
+    else if ( match(c.key(), "render-depth-offset-min-bias") ) {
+        style.getOrCreate<RenderSymbol>()->depthOffset()->minBias() = as<float>(c.value(), 100.0f);
+    }
+    else if ( match(c.key(), "render-depth-offset-max-bias") ) {
+        style.getOrCreate<RenderSymbol>()->depthOffset()->maxBias() = as<float>(c.value(), 10000.0f);
+    }
+    else if ( match(c.key(), "render-depth-offset-min-range") ) {
+        style.getOrCreate<RenderSymbol>()->depthOffset()->minRange() = as<float>(c.value(), 1000.0f);
+    }
+    else if ( match(c.key(), "render-depth-offset-max-range") ) {
+        style.getOrCreate<RenderSymbol>()->depthOffset()->maxRange() = as<float>(c.value(), 10000000.0f);
+    }
+}
diff --git a/src/osgEarthSymbology/Resource b/src/osgEarthSymbology/Resource
index b635f96..724fde6 100644
--- a/src/osgEarthSymbology/Resource
+++ b/src/osgEarthSymbology/Resource
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/Resource.cpp b/src/osgEarthSymbology/Resource.cpp
index 1d83029..6b64ef3 100644
--- a/src/osgEarthSymbology/Resource.cpp
+++ b/src/osgEarthSymbology/Resource.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ResourceCache b/src/osgEarthSymbology/ResourceCache
index 70342a7..2fc9f68 100644
--- a/src/osgEarthSymbology/ResourceCache
+++ b/src/osgEarthSymbology/ResourceCache
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ResourceCache.cpp b/src/osgEarthSymbology/ResourceCache.cpp
index 61d3fb1..4548090 100644
--- a/src/osgEarthSymbology/ResourceCache.cpp
+++ b/src/osgEarthSymbology/ResourceCache.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -40,8 +40,8 @@ ResourceCache::getStateSet( SkinResource* skin )
         {
             Threading::ScopedReadLock shared( _mutex );
 
-            SkinCache::Record rec = _skinCache.get( skin );
-            if ( rec.valid() )
+            SkinCache::Record rec;
+            if ( _skinCache.get(skin, rec) )
             {
                 result = rec.value();
             }
@@ -53,8 +53,8 @@ ResourceCache::getStateSet( SkinResource* skin )
             Threading::ScopedWriteLock exclusive( _mutex );
             
             // double check to avoid race condition
-            SkinCache::Record rec = _skinCache.get( skin );
-            if ( rec.valid() )
+            SkinCache::Record rec;
+            if ( _skinCache.get(skin, rec) )
             {
                 result = rec.value();
             }
@@ -70,8 +70,8 @@ ResourceCache::getStateSet( SkinResource* skin )
 
     else
     {
-        SkinCache::Record rec = _skinCache.get( skin );
-        if ( rec.valid() )
+        SkinCache::Record rec;
+        if ( _skinCache.get(skin, rec) )
         {
             result = rec.value();
         }
@@ -97,8 +97,8 @@ ResourceCache::getInstanceNode( InstanceResource* res )
         {
             Threading::ScopedReadLock shared( _mutex );
 
-            InstanceCache::Record rec = _instanceCache.get( res );
-            if ( rec.valid() )
+            InstanceCache::Record rec;
+            if ( _instanceCache.get(res, rec) )
             {
                 result = rec.value();
             }
@@ -110,8 +110,8 @@ ResourceCache::getInstanceNode( InstanceResource* res )
             Threading::ScopedWriteLock exclusive( _mutex );
             
             // double check to avoid race condition
-            InstanceCache::Record rec = _instanceCache.get( res );
-            if ( rec.valid() )
+            InstanceCache::Record rec;
+            if ( _instanceCache.get(res, rec) )
             {
                 result = rec.value();
             }
@@ -127,8 +127,8 @@ ResourceCache::getInstanceNode( InstanceResource* res )
 
     else
     {
-        InstanceCache::Record rec = _instanceCache.get( res );
-        if ( rec.valid() )
+        InstanceCache::Record rec;
+        if ( _instanceCache.get(res, rec) )
         {
             result = rec.value();
         }
@@ -155,8 +155,8 @@ ResourceCache::getMarkerNode( MarkerResource* marker )
         {
             Threading::ScopedReadLock shared( _mutex );
 
-            MarkerCache::Record rec = _markerCache.get( marker );
-            if ( rec.valid() )
+            MarkerCache::Record rec;
+            if ( _markerCache.get(marker, rec) )
             {
                 result = rec.value();
             }
@@ -168,8 +168,8 @@ ResourceCache::getMarkerNode( MarkerResource* marker )
             Threading::ScopedWriteLock exclusive( _mutex );
             
             // double check to avoid race condition
-            MarkerCache::Record rec = _markerCache.get( marker );
-            if ( rec.valid() )
+            MarkerCache::Record rec;
+            if ( _markerCache.get( marker, rec ) )
             {
                 result = rec.value();
             }
@@ -185,8 +185,8 @@ ResourceCache::getMarkerNode( MarkerResource* marker )
 
     else
     {
-        MarkerCache::Record rec = _markerCache.get( marker );
-        if ( rec.valid() )
+        MarkerCache::Record rec;
+        if ( _markerCache.get( marker, rec ) )
         {
             result = rec.value();
         }
diff --git a/src/osgEarthSymbology/ResourceLibrary b/src/osgEarthSymbology/ResourceLibrary
index 19ceb74..e4c83af 100644
--- a/src/osgEarthSymbology/ResourceLibrary
+++ b/src/osgEarthSymbology/ResourceLibrary
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/ResourceLibrary.cpp b/src/osgEarthSymbology/ResourceLibrary.cpp
index f1155be..670ce53 100644
--- a/src/osgEarthSymbology/ResourceLibrary.cpp
+++ b/src/osgEarthSymbology/ResourceLibrary.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/SLD.cpp b/src/osgEarthSymbology/SLD.cpp
deleted file mode 100644
index 47ba905..0000000
--- a/src/osgEarthSymbology/SLD.cpp
+++ /dev/null
@@ -1,446 +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 <osgEarthSymbology/SLD>
-#include <osgEarthSymbology/CssUtils>
-#include <osgEarthSymbology/Style>
-#include <osgEarthSymbology/Expression>
-#include <osgEarth/XmlUtils>
-#include <stack>
-#include <algorithm>
-
-using namespace osgEarth;
-using namespace osgEarth::Symbology;
-
-namespace
-{
-    void parseLineCap( const std::string& value, optional<Stroke::LineCapStyle>& cap )
-    {
-        if ( value == "butt" ) cap = Stroke::LINECAP_BUTT;
-        if ( value == "round" ) cap = Stroke::LINECAP_ROUND;
-        if ( value == "square" ) cap = Stroke::LINECAP_SQUARE;
-    }
-
-    bool match(const std::string& s, const char* reservedWord )
-    {
-        if ( s == reservedWord ) return true;
-        std::string temp1 = toLower(s), temp2 = toLower(reservedWord);
-        replaceIn(temp1, "_", "-");
-        replaceIn(temp2, "_", "-");
-        return temp1 == temp2;
-    }
-}
-
-
-bool
-SLDReader::readStyleFromCSSParams( const Config& conf, Style& sc )
-{
-    sc.setName( conf.key() );
-
-    IconSymbol*      icon      = 0L;
-    LineSymbol*      line      = 0L;
-    PolygonSymbol*   polygon   = 0L;
-    PointSymbol*     point     = 0L;
-    TextSymbol*      text      = 0L;
-    ExtrusionSymbol* extrusion = 0L;
-    MarkerSymbol*    marker    = 0L;
-    ModelSymbol*     model     = 0L;
-    AltitudeSymbol*  altitude  = 0L;
-    SkinSymbol*      skin      = 0L;
-
-    for( ConfigSet::const_iterator kids = conf.children().begin(); kids != conf.children().end(); ++kids )
-    {
-        const Config& p = *kids;
-
-        const std::string& key   = p.key();
-        const std::string& value = p.value();
-
-        // ..... LineSymbol .....
-
-        if ( match(key, "stroke") )
-        {
-            if (!line) line = sc.getOrCreateSymbol<LineSymbol>();
-            line->stroke()->color() = Color(value); //htmlColorToVec4f( value );
-        }
-        else if ( match(key, "stroke-opacity") )
-        {
-            if (!line) line = sc.getOrCreateSymbol<LineSymbol>();
-            line->stroke()->color().a() = as<float>( value, 1.0f );
-        }
-        else if ( match(key, "stroke-width") )
-        {
-            if (!line) line = sc.getOrCreateSymbol<LineSymbol>();
-            line->stroke()->width() = as<float>( value, 1.0f );
-        }
-        else if ( match(key, "stroke-linecap") )
-        {
-            if (!line) line = sc.getOrCreateSymbol<LineSymbol>();
-            parseLineCap( value, line->stroke()->lineCap() );
-        }
-        else if ( match(key, "stroke-tessellation") )
-        {
-            if (!line) line = sc.getOrCreate<LineSymbol>();
-            line->tessellation() = as<unsigned>( value, 0 );
-        }
-
-        // ..... PolygonSymbol .....
-
-        else if ( match(key, "fill") )
-        {
-            if (!polygon) polygon = sc.getOrCreateSymbol<PolygonSymbol>();
-            polygon->fill()->color() = htmlColorToVec4f( value );
-
-            if ( !point ) point = sc.getOrCreateSymbol<PointSymbol>();
-            point->fill()->color() = htmlColorToVec4f( value );
-
-            if ( !text ) text = new TextSymbol();
-            text->fill()->color() = htmlColorToVec4f( value );
-        }
-        else if ( match(key, "fill-opacity") )
-        {
-            if (!polygon) polygon = sc.getOrCreateSymbol<PolygonSymbol>();
-            polygon->fill()->color().a() = as<float>( value, 1.0f );
-
-            if ( !point ) point = sc.getOrCreateSymbol<PointSymbol>();
-            point->fill()->color().a() = as<float>( value, 1.0f );
-
-            if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
-            text->fill()->color().a() = as<float>( value, 1.0f );
-        }
-
-        // ..... PointSymbol .....
-
-        else if ( match(key, "point-size") )
-        {
-            if ( !point ) point = sc.getOrCreateSymbol<PointSymbol>();
-            point->size() = as<float>(value, 1.0f);
-        }
-
-        // ..... TextSymbol .....
-
-        else if ( match(key, "text-size") )
-        {
-            if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
-            text->size() = as<float>(value, 32.0f);
-        }
-        else if ( match(key, "text-font") )
-        {
-            if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
-            text->font() = value;
-        }
-        else if ( match(key, "text-halo") )
-        {
-            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>();
-            if (value == "true") text->removeDuplicateLabels() = true;
-            else if (value == "false") text->removeDuplicateLabels() = false;
-        } 
-        else if ( match(key, "text-align") )
-        {
-            if (!text) text = sc.getOrCreate<TextSymbol>();
-            if      ( match(value, "left-top") ) text->alignment() = TextSymbol::ALIGN_LEFT_TOP;
-            else if ( match(value, "left-center") ) text->alignment() = TextSymbol::ALIGN_LEFT_CENTER;
-            else if ( match(value, "left-bottom") ) text->alignment() = TextSymbol::ALIGN_LEFT_BOTTOM;
-            else if ( match(value, "center-top")  ) text->alignment() = TextSymbol::ALIGN_CENTER_TOP;
-            else if ( match(value, "center-center") ) text->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
-            else if ( match(value, "center-bottom") ) text->alignment() = TextSymbol::ALIGN_CENTER_BOTTOM;
-            else if ( match(value, "right-top") ) text->alignment() = TextSymbol::ALIGN_RIGHT_TOP;
-            else if ( match(value, "right-center") ) text->alignment() = TextSymbol::ALIGN_RIGHT_CENTER;
-            else if ( match(value, "right-bottom") ) text->alignment() = TextSymbol::ALIGN_RIGHT_BOTTOM;
-            else if ( match(value, "left-base-line") ) text->alignment() = TextSymbol::ALIGN_LEFT_BASE_LINE;
-            else if ( match(value, "center-base-line") ) text->alignment() = TextSymbol::ALIGN_CENTER_BASE_LINE;
-            else if ( match(value, "right-base-line") ) text->alignment() = TextSymbol::ALIGN_RIGHT_BASE_LINE;
-            else if ( match(value, "left-bottom-base-line") ) text->alignment() = TextSymbol::ALIGN_LEFT_BOTTOM_BASE_LINE;
-            else if ( match(value, "center-bottom-base-line") ) text->alignment() = TextSymbol::ALIGN_CENTER_BOTTOM_BASE_LINE;
-            else if ( match(value, "right-bottom-base-line") ) text->alignment() = TextSymbol::ALIGN_RIGHT_BOTTOM_BASE_LINE;
-            else if ( match(value, "base-line" ) ) text->alignment() = TextSymbol::ALIGN_BASE_LINE;
-        }
-        else if ( match(key, "text-content") )
-        {
-            if (!text) text = sc.getOrCreate<TextSymbol>();
-            text->content() = StringExpression( value );
-        }
-        else if ( match(key, "text-priority") )
-        {
-            if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
-            text->priority() = NumericExpression( value );
-        }
-        else if ( match(key, "text-provider") )
-        {
-            if (!text) text = sc.getOrCreate<TextSymbol>();
-            text->provider() = value;
-        }
-        else if ( match(key, "text-encoding") )
-        {
-            if (!text) text = sc.getOrCreateSymbol<TextSymbol>();
-            if      (match(value, "utf-8"))  text->encoding() = TextSymbol::ENCODING_UTF8;
-            else if (match(value, "utf-16")) text->encoding() = TextSymbol::ENCODING_UTF16;
-            else if (match(value, "utf-32")) text->encoding() = TextSymbol::ENCODING_UTF32;
-            else if (match(value, "ascii"))  text->encoding() = TextSymbol::ENCODING_ASCII;
-            else text->encoding() = TextSymbol::ENCODING_ASCII;
-        }
-        else if ( match(key, "text-declutter") )
-        {
-            if (!text) text = sc.getOrCreate<TextSymbol>();
-            text->declutter() = as<bool>(value, true);
-        }
-
-        // ..... MarkerSymbol .....
-
-        else if ( match(key, "marker") )
-        {
-            if (!marker) marker = sc.getOrCreate<MarkerSymbol>();            
-            marker->url() = value;
-            marker->url()->setURIContext( conf.referrer() );
-        }
-        else if ( match(key,"marker-library") )
-        {
-            if (!marker) marker = sc.getOrCreate<MarkerSymbol>();
-            marker->libraryName() = StringExpression(value);
-        }
-        else if ( match(key, "marker-placement") )
-        {
-            if (!marker) marker = sc.getOrCreate<MarkerSymbol>();
-            if      ( match(value, "vertex") )   marker->placement() = MarkerSymbol::PLACEMENT_VERTEX;
-            else if ( match(value, "interval") ) marker->placement() = MarkerSymbol::PLACEMENT_INTERVAL;
-            else if ( match(value, "random") )   marker->placement() = MarkerSymbol::PLACEMENT_RANDOM;
-            else if ( match(value, "centroid") ) marker->placement() = MarkerSymbol::PLACEMENT_CENTROID;
-        }
-        else if ( match(key, "marker-density") )
-        {
-            if (!marker) marker = sc.getOrCreateSymbol<MarkerSymbol>();
-            marker->density() = as<float>(value, 1.0f);
-        }
-        else if ( match(key, "marker-random-seed") )
-        {
-            if (!marker) marker = sc.getOrCreateSymbol<MarkerSymbol>();
-            marker->randomSeed() = as<unsigned>(value, 0);
-        }
-        else if ( match(key, "marker-scale") )
-        {
-            if (!marker) marker = sc.getOrCreateSymbol<MarkerSymbol>();
-            marker->scale() = NumericExpression(value);
-        }
-        else if ( match(key, "marker-icon-align") )
-        {
-            if (!marker) marker = sc.getOrCreate<MarkerSymbol>();
-            if      ( match(value, "left-top") ) marker->alignment() = MarkerSymbol::ALIGN_LEFT_TOP;
-            else if ( match(value, "left-center") ) marker->alignment() = MarkerSymbol::ALIGN_LEFT_CENTER;
-            else if ( match(value, "left-bottom") ) marker->alignment() = MarkerSymbol::ALIGN_LEFT_BOTTOM;
-            else if ( match(value, "center-top")  ) marker->alignment() = MarkerSymbol::ALIGN_CENTER_TOP;
-            else if ( match(value, "center-center") ) marker->alignment() = MarkerSymbol::ALIGN_CENTER_CENTER;
-            else if ( match(value, "center-bottom") ) marker->alignment() = MarkerSymbol::ALIGN_CENTER_BOTTOM;
-            else if ( match(value, "right-top") ) marker->alignment() = MarkerSymbol::ALIGN_RIGHT_TOP;
-            else if ( match(value, "right-center") ) marker->alignment() = MarkerSymbol::ALIGN_RIGHT_CENTER;
-            else if ( match(value, "right-bottom") ) marker->alignment() = MarkerSymbol::ALIGN_RIGHT_BOTTOM;
-        }
-
-        // ..... ModelSymbol .....
-
-        else if ( match(key, "model") )
-        {
-            if (!model) model = sc.getOrCreate<ModelSymbol>();
-            model->url() = value;
-            model->url()->setURIContext( conf.referrer() );
-        }
-        else if ( match(key, "model-placement") )
-        {
-            if (!model) model = sc.getOrCreate<ModelSymbol>();
-            if      ( match(value, "vertex") )   model->placement() = ModelSymbol::PLACEMENT_VERTEX;
-            else if ( match(value, "interval") ) model->placement() = ModelSymbol::PLACEMENT_INTERVAL;
-            else if ( match(value, "random") )   model->placement() = ModelSymbol::PLACEMENT_RANDOM;
-            else if ( match(value, "centroid") ) model->placement() = ModelSymbol::PLACEMENT_CENTROID;
-        }
-        else if ( match(key, "model-density") )
-        {
-            if (!model) model = sc.getOrCreate<ModelSymbol>();
-            model->density() = as<float>(value, 1.0f);
-        }
-        else if ( match(key, "model-random-seed") )
-        {
-            if (!model) model = sc.getOrCreate<ModelSymbol>();
-            model->randomSeed() = as<unsigned>(value, 0);
-        }
-        else if ( match(key, "model-scale") )
-        {
-            if (!model) model = sc.getOrCreate<ModelSymbol>();
-            model->scale() = NumericExpression(value);
-        }
-        else if ( match(key, "model-heading") )
-        {
-            if (!model) model = sc.getOrCreate<ModelSymbol>();
-            model->heading() = NumericExpression(value);
-        }
-
-        // ..... IconSymbol .....
-
-        else if ( match(key, "icon") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            icon->url() = value;
-            icon->url()->setURIContext( conf.referrer() );
-        }
-        else if ( match(key,"icon-library") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            icon->libraryName() = StringExpression(value);
-        }
-        else if ( match(key, "icon-placement") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            if      ( match(value, "vertex") )   icon->placement() = ModelSymbol::PLACEMENT_VERTEX;
-            else if ( match(value, "interval") ) icon->placement() = ModelSymbol::PLACEMENT_INTERVAL;
-            else if ( match(value, "random") )   icon->placement() = ModelSymbol::PLACEMENT_RANDOM;
-            else if ( match(value, "centroid") ) icon->placement() = ModelSymbol::PLACEMENT_CENTROID;
-        }
-        else if ( match(key, "icon-density") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            icon->density() = as<float>(value, 1.0f);
-        }
-        else if ( match(key, "icon-random-seed") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            icon->randomSeed() = as<unsigned>(value, 0);
-        }
-        else if ( match(key, "icon-scale") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            icon->scale() = NumericExpression(value);
-        }
-        else if ( match(key, "icon-align") )
-        {
-            if (!icon) icon = sc.getOrCreate<IconSymbol>();
-            if      ( match(value, "left-top") )      icon->alignment() = IconSymbol::ALIGN_LEFT_TOP;
-            else if ( match(value, "left-center") )   icon->alignment() = IconSymbol::ALIGN_LEFT_CENTER;
-            else if ( match(value, "left-bottom") )   icon->alignment() = IconSymbol::ALIGN_LEFT_BOTTOM;
-            else if ( match(value, "center-top")  )   icon->alignment() = IconSymbol::ALIGN_CENTER_TOP;
-            else if ( match(value, "center-center") ) icon->alignment() = IconSymbol::ALIGN_CENTER_CENTER;
-            else if ( match(value, "center-bottom") ) icon->alignment() = IconSymbol::ALIGN_CENTER_BOTTOM;
-            else if ( match(value, "right-top") )     icon->alignment() = IconSymbol::ALIGN_RIGHT_TOP;
-            else if ( match(value, "right-center") )  icon->alignment() = IconSymbol::ALIGN_RIGHT_CENTER;
-            else if ( match(value, "right-bottom") )  icon->alignment() = IconSymbol::ALIGN_RIGHT_BOTTOM;
-        }
-
-        // ..... ExtrusionSymbol .....
-                
-        else if ( match(key, "extrusion-height") )
-        {
-            if (!extrusion) extrusion = sc.getOrCreate<ExtrusionSymbol>();
-            extrusion->heightExpression() = NumericExpression(value);
-        }
-        else if ( match(key, "extrusion-flatten") )
-        {
-            if (!extrusion) extrusion = sc.getOrCreate<ExtrusionSymbol>();
-            extrusion->flatten() = as<bool>(value, true);
-        }
-        else if ( match(key, "extrusion-wall-style") )
-        {
-            if (!extrusion) extrusion = sc.getOrCreate<ExtrusionSymbol>();
-            extrusion->wallStyleName() = value;
-        }
-        else if ( match(key, "extrusion-roof-style") )
-        {
-            if (!extrusion) extrusion = sc.getOrCreate<ExtrusionSymbol>();
-            extrusion->roofStyleName() = value;
-        }
-        else if ( match(key, "extrusion-wall-gradient") )
-        {
-            if (!extrusion) extrusion = sc.getOrCreate<ExtrusionSymbol>();
-            extrusion->wallGradientPercentage() = as<float>(value, 0.0f);
-        }
-
-        // ..... AltitideSymbol .....
-                
-        else if ( match(key, "altitude-clamping") )
-        {
-            if (!altitude) altitude = sc.getOrCreateSymbol<AltitudeSymbol>();
-            if      ( match(value, "none") )     altitude->clamping() = AltitudeSymbol::CLAMP_NONE;
-            else if ( match(value, "terrain") )  altitude->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
-            else if ( match(value, "absolute") ) altitude->clamping() = AltitudeSymbol::CLAMP_ABSOLUTE;
-            else if ( match(value, "relative") ) altitude->clamping() = AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN;
-        }
-        else if ( match(key, "altitude-resolution") )
-        {
-            if (!altitude) altitude = sc.getOrCreate<AltitudeSymbol>();
-            altitude->clampingResolution() = as<float>( value, 0.0f );
-        }
-        else if ( match(key, "altitude-offset") )
-        {
-            if (!altitude) altitude = sc.getOrCreate<AltitudeSymbol>();
-            altitude->verticalOffset() = NumericExpression( value );
-        }
-        else if ( match(key, "altitude-scale") )
-        {
-            if (!altitude) altitude = sc.getOrCreate<AltitudeSymbol>();
-            altitude->verticalScale() = NumericExpression( value );
-        }
-
-        // ..... SkinSymbol .....
-
-        else if ( match(key, "skin-library") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            if ( !value.empty() ) skin->libraryName() = value;
-        }
-        else if ( match(key, "skin-tags") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            skin->addTags( value );
-        }
-        else if ( match(key, "skin-tiled") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            skin->isTiled() = as<bool>( value, false );
-        }
-        else if ( match(key, "skin-object-height") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            skin->objectHeight() = as<float>( value, 0.0f );
-        }
-        else if (match(key, "skin-min-object-height") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            skin->minObjectHeight() = as<float>( value, 0.0f );
-        }
-        else if (match(key, "skin-max-object-height") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            skin->maxObjectHeight() = as<float>( value, 0.0f );
-        }
-        else if (match(key, "skin-random-seed") )
-        {
-            if (!skin) skin = sc.getOrCreate<SkinSymbol>();
-            skin->randomSeed() = as<unsigned>( value, 0u );
-        }
-
-    }
-
-    return true;
-}
-
-
diff --git a/src/osgEarthSymbology/Skins b/src/osgEarthSymbology/Skins
index 067f489..72bedab 100644
--- a/src/osgEarthSymbology/Skins
+++ b/src/osgEarthSymbology/Skins
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -112,6 +112,8 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT SkinSymbol : public Taggable<Symbol>
     {
     public:
+        META_Symbol(SkinSymbol);
+
         SkinSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -146,6 +148,7 @@ namespace osgEarth { namespace Symbology
     public:
         void mergeConfig(const Config& conf);
         Config getConfig() const;
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<std::string> _libraryName;
diff --git a/src/osgEarthSymbology/Skins.cpp b/src/osgEarthSymbology/Skins.cpp
index 26b77bc..3d20fd5 100644
--- a/src/osgEarthSymbology/Skins.cpp
+++ b/src/osgEarthSymbology/Skins.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/Skins>
+#include <osgEarthSymbology/Style>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ImageUtils>
 #include <osgEarth/Registry>
@@ -172,3 +173,31 @@ SkinSymbol::getConfig() const
 
     return conf;
 }
+
+
+void
+SkinSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "skin-library") ) {
+        if ( !c.value().empty() ) 
+            style.getOrCreate<SkinSymbol>()->libraryName() = c.value();
+    }
+    else if ( match(c.key(), "skin-tags") ) {
+        style.getOrCreate<SkinSymbol>()->addTags( c.value() );
+    }
+    else if ( match(c.key(), "skin-tiled") ) {
+        style.getOrCreate<SkinSymbol>()->isTiled() = as<bool>( c.value(), false );
+    }
+    else if ( match(c.key(), "skin-object-height") ) {
+        style.getOrCreate<SkinSymbol>()->objectHeight() = as<float>( c.value(), 0.0f );
+    }
+    else if (match(c.key(), "skin-min-object-height") ) {
+        style.getOrCreate<SkinSymbol>()->minObjectHeight() = as<float>( c.value(), 0.0f );
+    }
+    else if (match(c.key(), "skin-max-object-height") ) {
+        style.getOrCreate<SkinSymbol>()->maxObjectHeight() = as<float>( c.value(), 0.0f );
+    }
+    else if (match(c.key(), "skin-random-seed") ) {
+        style.getOrCreate<SkinSymbol>()->randomSeed() = as<unsigned>( c.value(), 0u );
+    }
+}
diff --git a/src/osgEarthSymbology/StencilVolumeNode b/src/osgEarthSymbology/StencilVolumeNode
index f385e7c..3afa0cf 100644
--- a/src/osgEarthSymbology/StencilVolumeNode
+++ b/src/osgEarthSymbology/StencilVolumeNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/StencilVolumeNode.cpp b/src/osgEarthSymbology/StencilVolumeNode.cpp
index 9f02199..b4d1847 100644
--- a/src/osgEarthSymbology/StencilVolumeNode.cpp
+++ b/src/osgEarthSymbology/StencilVolumeNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/Stroke b/src/osgEarthSymbology/Stroke
new file mode 100644
index 0000000..a2957e9
--- /dev/null
+++ b/src/osgEarthSymbology/Stroke
@@ -0,0 +1,118 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 OSGEARTHSYMBOLOGY_STROKE_H
+#define OSGEARTHSYMBOLOGY_STROKE_H 1
+
+#include <osgEarthSymbology/Common>
+#include <osgEarthSymbology/Color>
+#include <osgEarth/Config>
+#include <osgEarth/Units>
+
+
+namespace osgEarth { namespace Symbology
+{
+    /**
+     * Drawing parameters for a line.
+     */
+    class OSGEARTHSYMBOLOGY_EXPORT Stroke
+    {
+    public:
+
+        /** Style for rendering the end caps of a line string. */
+        enum LineCapStyle
+        {
+            LINECAP_FLAT,   /** no endcap. the line ends at the terminal point. */
+            LINECAP_SQUARE, /** endcap extends width()/2 past the terminal point and is squared off. */
+            LINECAP_ROUND   /** endcap extends width()/2 past the terminal point and is rounded off. */
+        };
+
+        /** Style for rendering the joins between line segments. */
+        enum LineJoinStyle
+        {
+            LINEJOIN_MITRE, /** outside joins form a sharp point. */
+            LINEJOIN_ROUND  /** outside joins form an arc. */
+        };
+
+    public:
+        Stroke();
+        Stroke(float r, float g, float b, float a );
+        Stroke(const Color& color );
+        Stroke(const Config& conf );
+        Stroke(const Stroke& rhs);
+
+        virtual ~Stroke() { }
+
+        /** Line color. */
+        Color& color() { return _color; }
+        const Color& color() const { return _color; }
+
+        /** Capping of line ends. */
+        optional<LineCapStyle>& lineCap() { return _lineCap; }
+        const optional<LineCapStyle>& lineCap() const { return _lineCap; }
+
+        /** How to render line joints in a LineString. */
+        optional<LineJoinStyle>& lineJoin() { return _lineJoin; }
+        const optional<LineJoinStyle>& lineJoin() const { return _lineJoin; }
+
+        /** Line rendering width. */
+        optional<float>& width() { return _width; }
+        const optional<float>& width() const { return _width; }
+
+        /** Units for the width property. (default = Units::PIXELS) */
+        optional<Units>& widthUnits() { return _widthUnits; }
+        const optional<Units>& widthUnits() const { return _widthUnits; }
+
+        /** Minimum with of a line in pixels (default = 0.0, no minimum).
+            This typically only applies to lines with map unit width, and
+            tells the renderer to maintain a minimum pixel width so that 
+            the geometry is always visible. */
+        optional<float>& minPixels() { return _minPixels; }
+        const optional<float>& minPixels() const { return _minPixels; }
+
+        /** Stippling pattern. */
+        optional<unsigned short>& stipple() { return _stipple;}
+        const optional<unsigned short>& stipple() const { return _stipple; }
+
+        /** Rounding ratio - when rounding corners/caps, this is the ratio
+            of rounded-segment length to width(). The smaller this value,
+            the more detailed the rounding will be. (Default is 0.4) */
+        optional<float>& roundingRatio() { return _roundingRatio; }
+        const optional<float>& roundingRatio() const { return _roundingRatio; }
+
+    public:
+        virtual Config getConfig() const;
+        virtual void mergeConfig( const Config& conf );
+
+    protected:
+        Color                    _color;
+        optional<LineCapStyle>   _lineCap;
+        optional<LineJoinStyle>  _lineJoin;
+        optional<float>          _width;
+        optional<Units>          _widthUnits;
+        optional<unsigned short> _stipple;
+        optional<float>          _roundingRatio;
+        optional<float>          _minPixels;
+
+        void init();
+    };
+
+} } // namespace osgEarth::Symbology
+
+#endif // OSGEARTHSYMBOLOGY_STROKE_H
diff --git a/src/osgEarthSymbology/Stroke.cpp b/src/osgEarthSymbology/Stroke.cpp
new file mode 100644
index 0000000..117af67
--- /dev/null
+++ b/src/osgEarthSymbology/Stroke.cpp
@@ -0,0 +1,100 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2013 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 <osgEarthSymbology/Stroke>
+
+using namespace osgEarth;
+using namespace osgEarth::Symbology;
+
+//------------------------------------------------------------------------
+
+Stroke::Stroke()
+{
+    init();
+}
+
+Stroke::Stroke( float r, float g, float b, float a )
+{
+    init();
+    _color.set( r, g, b, a );
+}
+
+Stroke::Stroke(const Color& color)
+{
+    init();
+    _color = color;
+}
+
+Stroke::Stroke(const Config& conf)
+{
+    init();
+    mergeConfig( conf );
+}
+
+Stroke::Stroke(const Stroke& rhs)
+{
+    init();
+    mergeConfig( rhs.getConfig() );
+}
+
+void
+Stroke::init()
+{
+    _color.set         ( 1.0f, 1.0f, 1.0f, 1.0f );
+    _lineCap.init      ( LINECAP_FLAT );
+    _lineJoin.init     ( LINEJOIN_ROUND );
+    _width.init        ( 1.0f );
+    _widthUnits.init   ( Units::PIXELS );
+    _roundingRatio.init( 0.4f );
+    _minPixels.init    ( 0.0f );
+}
+
+Config 
+Stroke::getConfig() const {
+    Config conf("stroke");
+    conf.add( "color", _color.toHTML() );
+    conf.addIfSet("linecap", "flat",   _lineCap, LINECAP_FLAT);
+    conf.addIfSet("linecap", "square", _lineCap, LINECAP_SQUARE);
+    conf.addIfSet("linecap", "round",  _lineCap, LINECAP_ROUND);
+    conf.addIfSet("linejoin", "mitre", _lineJoin, LINEJOIN_MITRE);
+    conf.addIfSet("linejoin", "round", _lineJoin, LINEJOIN_ROUND);
+    conf.addIfSet("width", _width);
+    conf.addIfSet("stipple", _stipple);
+    conf.addIfSet("rounding_ratio", _roundingRatio);
+    if ( _widthUnits.isSet() )
+        conf.add( "width_units", _widthUnits->getAbbr() );
+    conf.addIfSet("min_pixels", _minPixels );
+    return conf;
+}
+
+void 
+Stroke::mergeConfig( const Config& conf ) {
+    _color = Color( conf.value("color") );
+    conf.getIfSet("linecap", "flat",   _lineCap, LINECAP_FLAT);
+    conf.getIfSet("linecap", "square", _lineCap, LINECAP_SQUARE);
+    conf.getIfSet("linecap", "round",  _lineCap, LINECAP_ROUND);
+    conf.getIfSet("linejoin", "mitre", _lineJoin, LINEJOIN_MITRE);
+    conf.getIfSet("linejoin", "miter", _lineJoin, LINEJOIN_MITRE); // alternate spelling
+    conf.getIfSet("linejoin", "round", _lineJoin, LINEJOIN_ROUND);
+    conf.getIfSet("width", _width);
+    conf.getIfSet("stipple", _stipple);
+    conf.getIfSet("rounding_ratio", _roundingRatio);
+    if ( conf.hasValue("width_units" ) )
+        Units::parse( conf.value("width_units"), _widthUnits.mutable_value() );
+    conf.getIfSet("min_pixels", _minPixels );
+}
diff --git a/src/osgEarthSymbology/Style b/src/osgEarthSymbology/Style
index 7ad1576..8e2a96c 100644
--- a/src/osgEarthSymbology/Style
+++ b/src/osgEarthSymbology/Style
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -32,6 +32,7 @@
 #include <osgEarthSymbology/AltitudeSymbol>
 #include <osgEarthSymbology/TextSymbol>
 #include <osgEarthSymbology/Skins>
+#include <osgEarthSymbology/RenderSymbol>
 #include <osgEarthSymbology/ResourceLibrary>
 
 #include <osgEarth/Config>
@@ -83,6 +84,14 @@ namespace osgEarth { namespace Symbology
         /** Remove a symbol from the collection */
         bool removeSymbol (Symbol* symbol);
 
+        /** Remove a symbol by type */
+        template<typename T>
+        bool remove()
+        {
+            Symbol* s = get<T>();
+            return s ? removeSymbol(s) : false;
+        }
+
         /** Gets a typed symbol from the style (the first one found). */
         template<typename T>
         T* getSymbol()
@@ -130,14 +139,17 @@ namespace osgEarth { namespace Symbology
             }
             return sym;
         }
-        template<typename T> T* getOrCreate() { return getOrCreateSymbol<T>(); } // alias        
+
+        template<typename T> T* getOrCreate() { return getOrCreateSymbol<T>(); } // alias
+
 
         /** Serializes this object into a Config. */
         virtual Config getConfig( bool keepOrigType =true ) const;
 
         void mergeConfig( const Config& conf );
-        void fromCSS( const std::string& css );
 
+        void fromSLD(const Config&);
+        
     protected:
         std::string                 _name;
         SymbolList                  _symbols;
diff --git a/src/osgEarthSymbology/Style.cpp b/src/osgEarthSymbology/Style.cpp
index 1dfd380..ee94120 100644
--- a/src/osgEarthSymbology/Style.cpp
+++ b/src/osgEarthSymbology/Style.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,7 +18,6 @@
 */
 #include <osgEarthSymbology/Style>
 #include <osgEarthSymbology/CssUtils>
-#include <osgEarthSymbology/SLD>
 #include <algorithm>
 
 using namespace osgEarth;
@@ -68,7 +67,18 @@ Style::operator = ( const Style& rhs )
 void Style::addSymbol(Symbol* symbol)
 {
     if ( symbol )
+    {
+        for( SymbolList::iterator i = _symbols.begin(); i != _symbols.end(); ++i )
+        {
+            if ( i->get()->isSameKindAs(symbol) )
+            {
+                (*i) = symbol;
+                return;
+            }
+        }
+
         _symbols.push_back(symbol);
+    }
 }
 
 bool Style::removeSymbol(Symbol* symbol)
@@ -110,9 +120,26 @@ Style::combineWith( const Style& rhs ) const
 }
 
 void
-Style::fromCSS( const std::string& css )
+Style::fromSLD( const Config& sld )
 {
-    SLDReader::readStyleFromCSSParams( css, *this );
+    setName( sld.key() );
+
+    for( ConfigSet::const_iterator kid = sld.children().begin(); kid != sld.children().end(); ++kid )
+    {
+        const Config& p = *kid;
+
+        AltitudeSymbol::parseSLD (p, *this);
+        ExtrusionSymbol::parseSLD(p, *this);
+        IconSymbol::parseSLD     (p, *this);
+        LineSymbol::parseSLD     (p, *this);
+        MarkerSymbol::parseSLD   (p, *this);
+        ModelSymbol::parseSLD    (p, *this);
+        PolygonSymbol::parseSLD  (p, *this);
+        PointSymbol::parseSLD    (p, *this);
+        RenderSymbol::parseSLD   (p, *this);
+        SkinSymbol::parseSLD     (p, *this);
+        TextSymbol::parseSLD     (p, *this);
+    }
 }
 
 void
@@ -128,16 +155,23 @@ Style::mergeConfig( const Config& conf )
     conf.getIfSet( "url", _uri ); // named "url" for back compat
 
     _origType = conf.value( "type" );
+    std::string textData = trim(conf.value());
+
+    bool useCSS =
+        _origType.compare("text/css") == 0 ||
+        !textData.empty();
 
-    if ( conf.value( "type" ) == "text/css" )
+    if ( useCSS )
     {
-        _origData = conf.value();
+        _origData = textData;
         
         // just take the first block.
         ConfigSet blocks;
         CssUtils::readConfig( _origData, conf.referrer(), blocks );
         if ( blocks.size() > 0 )
-            SLDReader::readStyleFromCSSParams( blocks.front(), *this );
+        {
+            fromSLD( blocks.front() );
+        }
     }
     else
     {
@@ -151,51 +185,45 @@ Style::mergeConfig( const Config& conf )
                 if ( c.key() == "text" )
                 {
                     add( new TextSymbol(c) );
-                    //getOrCreate<TextSymbol>()->mergeConfig( c );
                 }
                 else if ( c.key() == "point" )
                 {
                     add( new PointSymbol(c) );
-                    //getOrCreate<PointSymbol>()->mergeConfig( c );
                 }
                 else if ( c.key() == "line" )
                 {
-                    //getOrCreate<LineSymbol>()->mergeConfig( c );
                     add( new LineSymbol(c) );
                 }
                 else if ( c.key() == "polygon" )
                 {
-                    //getOrCreate<PolygonSymbol>()->mergeConfig( c );
                     add( new PolygonSymbol(c) );
                 }
                 else if ( c.key() == "extrusion" )
                 {
-                    //getOrCreate<ExtrusionSymbol>()->mergeConfig( c );
                     add( new ExtrusionSymbol(c) );
                 }
                 else if ( c.key() == "altitude" )
                 {
-                    //getOrCreate<AltitudeSymbol>()->mergeConfig( c );
                     add( new AltitudeSymbol(c) );
                 }
                 else if ( c.key() == "marker" )
                 {
-                    //getOrCreate<MarkerSymbol>()->mergeConfig( c );
                     add( new MarkerSymbol(c) );
                 }
+                else if ( c.key() == "render" )
+                {
+                    add( new RenderSymbol(c) );
+                }
                 else if ( c.key() == "skin" )
                 {
-                    //getOrCreate<SkinSymbol>()->mergeConfig( c );
                     add( new SkinSymbol(c) );
                 }
                 else if ( c.key() == "model" )
                 {
-                    //getOrCreate<ModelSymbol>()->mergeConfig( c );
                     add( new ModelSymbol(c) );
                 }
                 else if ( c.key() == "icon" )
                 {
-                    //getOrCreate<IconSymbol>()->mergeConfig( c );
                     add( new IconSymbol(c) );
                 }
             }
diff --git a/src/osgEarthSymbology/StyleSelector b/src/osgEarthSymbology/StyleSelector
index 750b689..f8000ac 100644
--- a/src/osgEarthSymbology/StyleSelector
+++ b/src/osgEarthSymbology/StyleSelector
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/StyleSelector.cpp b/src/osgEarthSymbology/StyleSelector.cpp
index 1093aac..1d68e03 100644
--- a/src/osgEarthSymbology/StyleSelector.cpp
+++ b/src/osgEarthSymbology/StyleSelector.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/StyleSheet b/src/osgEarthSymbology/StyleSheet
index 93bb425..f97934f 100644
--- a/src/osgEarthSymbology/StyleSheet
+++ b/src/osgEarthSymbology/StyleSheet
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/StyleSheet.cpp b/src/osgEarthSymbology/StyleSheet.cpp
index 7036ab9..49275d6 100644
--- a/src/osgEarthSymbology/StyleSheet.cpp
+++ b/src/osgEarthSymbology/StyleSheet.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,7 +18,6 @@
 */
 #include <osgEarthSymbology/StyleSheet>
 #include <osgEarthSymbology/CssUtils>
-#include <osgEarthSymbology/SLD>
 #include <algorithm>
 
 #define LC "[StyleSheet] "
diff --git a/src/osgEarthSymbology/Symbol b/src/osgEarthSymbology/Symbol
index 77dedda..b6bb498 100644
--- a/src/osgEarthSymbology/Symbol
+++ b/src/osgEarthSymbology/Symbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -26,6 +26,11 @@
 #include <osg/Referenced>
 #include <vector>
 
+#define META_Symbol(name) \
+    virtual bool isSameKindAs(const Symbol* rhs) const { return dynamic_cast<const name *>(rhs)!=NULL; } \
+    virtual const char* className() const { return #name; }
+
+
 namespace osgEarth { namespace Symbology
 {
     /**
@@ -34,101 +39,24 @@ namespace osgEarth { namespace Symbology
     class OSGEARTHSYMBOLOGY_EXPORT Symbol : public osg::Referenced
     {
     public:
-        Symbol( const Config& conf =Config() );
-        
-        virtual ~Symbol() { }
-
         const URIContext& uriContext() const { return _uriContext; }
 
         virtual Config getConfig() const { return Config(); }
 
-    protected:
-        URIContext _uriContext;
-    };
-    typedef std::vector<osg::ref_ptr<Symbol> > SymbolList;
-
-    //--------------------------------------------------------------------
-    
-    /**
-     * Simple drawing parameters for a line.
-     */
-    class OSGEARTHSYMBOLOGY_EXPORT Stroke
-    {
-    public:
-        enum LineCapStyle {
-            LINECAP_DEFAULT,
-            LINECAP_BUTT,
-            LINECAP_SQUARE,
-            LINECAP_ROUND
-        };
-
-        enum LineJoinStyle {
-            LINEJOIN_DEFAULT
-        };
-
-    public:
-        Stroke();
-        Stroke( float r, float g, float b, float a );
-        Stroke( const Config& conf ) { mergeConfig(conf); }
-
-        virtual ~Stroke() { }
-
-        /** Line color. */
-        osg::Vec4f& color() { return _color; }
-        const osg::Vec4f& color() const { return _color; }
-
-        /** Capping of line ends. */
-        optional<LineCapStyle>& lineCap() { return _lineCap; }
-        const optional<LineCapStyle>& lineCap() const { return _lineCap; }
-
-        /** How to render line joints in a LineString. */
-        optional<LineJoinStyle>& lineJoin() { return _lineJoin; }
-        const optional<LineJoinStyle>& lineJoin() const { return _lineJoin; }
-
-        /** Line rendering width. */
-        optional<float>& width() { return _width; }        
-        const optional<float>& width() const { return _width; }
-
-        /** Stippling pattern. */
-        optional<unsigned short>& stipple() { return _stipple;}
-        const optional<unsigned short>& stipple() const { return _stipple; }
-
-    public:
-        virtual Config getConfig() const;            
-        virtual void mergeConfig( const Config& conf );
+    public: // META_Symbol methods
+        virtual bool isSameKindAs(const Symbol* sym) const =0;
+        virtual const char* className() const =0;
 
     protected:
-        osg::Vec4f               _color;
-        optional<LineCapStyle>   _lineCap;
-        optional<LineJoinStyle>  _lineJoin;
-        optional<float>          _width;
-        optional<unsigned short> _stipple;
-    };
-
-    //--------------------------------------------------------------------
-
-    /**
-     * Simple drawing parameters for a filled area.
-     */
-    class OSGEARTHSYMBOLOGY_EXPORT Fill
-    {
-    public:
-        Fill();
-        Fill( float r, float g, float b, float a );
-        Fill( const Config& conf ) { mergeConfig(conf); }
-
-        virtual ~Fill() { }
+        URIContext _uriContext;
 
-        osg::Vec4f& color() { return _color; }
-        const osg::Vec4f& color() const { return _color; }
+        static bool match(const std::string& key, const char* pattern);
 
-    public:
-        virtual Config getConfig() const;
-        virtual void mergeConfig( const Config& conf );
+        Symbol( const Config& conf =Config() );
 
-    protected:
-        osg::Vec4f _color;
+        virtual ~Symbol() { }
     };
+    typedef std::vector<osg::ref_ptr<Symbol> > SymbolList;
 
 } } // namespace osgEarth::Symbology
 
diff --git a/src/osgEarthSymbology/Symbol.cpp b/src/osgEarthSymbology/Symbol.cpp
index d9cce89..28cf3d6 100644
--- a/src/osgEarthSymbology/Symbol.cpp
+++ b/src/osgEarthSymbology/Symbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -28,72 +28,13 @@ Symbol::Symbol( const Config& conf )
     _uriContext = URIContext(conf.referrer());
 }
 
-//------------------------------------------------------------------------
-
-Stroke::Stroke() :
-_color( 1, 1, 1, 1 ),
-_lineCap( LINECAP_DEFAULT ),
-_lineJoin( LINEJOIN_DEFAULT ),
-_width( 1.0f )
-{
-    //nop
-}
-
-Stroke::Stroke( float r, float g, float b, float a ) :
-_color( r, g, b, a ),
-_lineCap( LINECAP_DEFAULT ),
-_lineJoin( LINEJOIN_DEFAULT ),
-_width( 1.0f )
-{
-    //nop
-}
-
-Config 
-Stroke::getConfig() const {
-    Config conf("stroke");
-    conf.add( "color", vec4fToHtmlColor(_color) );
-    conf.addIfSet("linecap", "butt", _lineCap, LINECAP_BUTT);
-    conf.addIfSet("linecap", "square", _lineCap, LINECAP_SQUARE);
-    conf.addIfSet("linecap", "round", _lineCap, LINECAP_ROUND);
-    conf.addIfSet("width", _width);
-    conf.addIfSet("stipple", _stipple);
-    return conf;
-}
-
-void 
-Stroke::mergeConfig( const Config& conf ) {
-    _color = htmlColorToVec4f( conf.value("color") );
-    conf.getIfSet("linecap", "butt", _lineCap, LINECAP_BUTT);
-    conf.getIfSet("linecap", "square", _lineCap, LINECAP_SQUARE);
-    conf.getIfSet("linecap", "round", _lineCap, LINECAP_ROUND);
-    conf.getIfSet("width", _width);
-    conf.getIfSet("stipple", _stipple);
-}
-
-//------------------------------------------------------------------------
-
-Fill::Fill( float r, float g, float b, float a ) :
-_color( r, g, b, a )
-{
-    //nop
-}
-
-Fill::Fill() :
-_color( 1, 1, 1, 1 )
-{
-    //nop
-}
-
-Config
-Fill::getConfig() const
-{
-    Config conf("fill");
-    conf.add("color", vec4fToHtmlColor(_color));
-    return conf;
-}
-
-void
-Fill::mergeConfig( const Config& conf )
+bool
+Symbol::match(const std::string& s, const char* reservedWord)
 {
-    _color = htmlColorToVec4f(conf.value("color"));
+    if ( s.compare(reservedWord) == 0 ) return true;
+    //if ( s == reservedWord ) return true;
+    std::string temp1 = toLower(s), temp2 = toLower(reservedWord);
+    replaceIn(temp1, "_", "-");
+    replaceIn(temp2, "_", "-");
+    return temp1.compare(temp2) == 0;
 }
diff --git a/src/osgEarthSymbology/Tags b/src/osgEarthSymbology/Tags
index 406832d..ba6a3f2 100644
--- a/src/osgEarthSymbology/Tags
+++ b/src/osgEarthSymbology/Tags
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthSymbology/TextSymbol b/src/osgEarthSymbology/TextSymbol
index 2b511b7..ccede0b 100644
--- a/src/osgEarthSymbology/TextSymbol
+++ b/src/osgEarthSymbology/TextSymbol
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -22,8 +22,8 @@
 
 #include <osgEarthSymbology/Symbol>
 #include <osgEarthSymbology/Expression>
-#include <osg/Referenced>
-#include <vector>
+#include <osgEarthSymbology/Fill>
+#include <osgEarthSymbology/Stroke>
 
 namespace osgEarth { namespace Symbology
 {
@@ -65,6 +65,8 @@ namespace osgEarth { namespace Symbology
             ALIGN_BASE_LINE = ALIGN_LEFT_BASE_LINE /// default.        
         };
 
+        META_Symbol(TextSymbol);
+
         TextSymbol( const Config& conf =Config() );
 
         /** dtor */
@@ -125,6 +127,7 @@ namespace osgEarth { namespace Symbology
     public:
         virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
+        static void parseSLD(const Config& c, class Style& style);
 
     protected:
         optional<Fill>              _fill;
diff --git a/src/osgEarthSymbology/TextSymbol.cpp b/src/osgEarthSymbology/TextSymbol.cpp
index 08f0834..9b732fd 100644
--- a/src/osgEarthSymbology/TextSymbol.cpp
+++ b/src/osgEarthSymbology/TextSymbol.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarthSymbology/TextSymbol>
+#include <osgEarthSymbology/Style>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -124,3 +125,91 @@ TextSymbol::mergeConfig( const Config& conf )
     if ( conf.hasValue( "pixel_offset_y" ) )
         _pixelOffset = osg::Vec2s( _pixelOffset->x(), conf.value<short>("pixel_offset_y",0) );
 }
+
+
+void
+TextSymbol::parseSLD(const Config& c, Style& style)
+{
+    if ( match(c.key(), "fill") ) {
+        style.getOrCreate<TextSymbol>()->fill()->color() = Color(c.value());
+    }
+    else if ( match(c.key(), "fill-opacity") ) {
+        style.getOrCreate<TextSymbol>()->fill()->color().a() = as<float>( c.value(), 1.0f );
+    }
+    else if ( match(c.key(), "text-size") ) {
+        style.getOrCreate<TextSymbol>()->size() = as<float>(c.value(), 32.0f);
+    }
+    else if ( match(c.key(), "text-font") ) {
+        style.getOrCreate<TextSymbol>()->font() = c.value();
+    }
+    else if ( match(c.key(), "text-halo") ) {
+        style.getOrCreate<TextSymbol>()->halo()->color() = htmlColorToVec4f( c.value() );
+    }
+    else if ( match(c.key(), "text-halo-offset") ) {
+        style.getOrCreate<TextSymbol>()->haloOffset() = as<float>(c.value(), 0.07f);
+    }
+    else if ( match(c.key(), "text-remove-duplicate-labels") ) {
+        if ( c.value() == "true" )
+            style.getOrCreate<TextSymbol>()->removeDuplicateLabels() = true;
+        else if (c.value() == "false")
+            style.getOrCreate<TextSymbol>()->removeDuplicateLabels() = false;
+    } 
+    else if ( match(c.key(), "text-align") ) {
+        if      ( match(c.value(), "left-top") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_LEFT_TOP;
+        else if ( match(c.value(), "left-center") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_LEFT_CENTER;
+        else if ( match(c.value(), "left-bottom") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_LEFT_BOTTOM;
+        else if ( match(c.value(), "center-top")  ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_TOP;
+        else if ( match(c.value(), "center-center") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
+        else if ( match(c.value(), "center-bottom") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_BOTTOM;
+        else if ( match(c.value(), "right-top") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_RIGHT_TOP;
+        else if ( match(c.value(), "right-center") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_RIGHT_CENTER;
+        else if ( match(c.value(), "right-bottom") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_RIGHT_BOTTOM;
+        else if ( match(c.value(), "left-base-line") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_LEFT_BASE_LINE;
+        else if ( match(c.value(), "center-base-line") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_BASE_LINE;
+        else if ( match(c.value(), "right-base-line") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_RIGHT_BASE_LINE;
+        else if ( match(c.value(), "left-bottom-base-line") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_LEFT_BOTTOM_BASE_LINE;
+        else if ( match(c.value(), "center-bottom-base-line") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_BOTTOM_BASE_LINE;
+        else if ( match(c.value(), "right-bottom-base-line") ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_RIGHT_BOTTOM_BASE_LINE;
+        else if ( match(c.value(), "base-line" ) ) 
+            style.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_BASE_LINE;
+    }
+    else if ( match(c.key(), "text-content") ) {        
+        style.getOrCreate<TextSymbol>()->content() = StringExpression( c.value() );
+    }
+    else if ( match(c.key(), "text-priority") ) {
+        style.getOrCreate<TextSymbol>()->priority() = NumericExpression( c.value() );
+    }
+    else if ( match(c.key(), "text-provider") ) {
+        style.getOrCreate<TextSymbol>()->provider() = c.value();
+    }
+    else if ( match(c.key(), "text-encoding") ) {
+        if      (match(c.value(), "utf-8"))  
+            style.getOrCreate<TextSymbol>()->encoding() = TextSymbol::ENCODING_UTF8;
+        else if (match(c.value(), "utf-16")) 
+            style.getOrCreate<TextSymbol>()->encoding() = TextSymbol::ENCODING_UTF16;
+        else if (match(c.value(), "utf-32")) 
+            style.getOrCreate<TextSymbol>()->encoding() = TextSymbol::ENCODING_UTF32;
+        else if (match(c.value(), "ascii"))  
+            style.getOrCreate<TextSymbol>()->encoding() = TextSymbol::ENCODING_ASCII;
+        else 
+            style.getOrCreate<TextSymbol>()->encoding() = TextSymbol::ENCODING_ASCII;
+    }
+    else if ( match(c.key(), "text-declutter") ) {
+        style.getOrCreate<TextSymbol>()->declutter() = as<bool>(c.value(), true);
+    }
+}
diff --git a/src/osgEarthUtil/AnnotationEvents b/src/osgEarthUtil/AnnotationEvents
index 32a269e..fb2d4d3 100644
--- a/src/osgEarthUtil/AnnotationEvents
+++ b/src/osgEarthUtil/AnnotationEvents
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/AnnotationEvents.cpp b/src/osgEarthUtil/AnnotationEvents.cpp
index 5b94568..07626b7 100644
--- a/src/osgEarthUtil/AnnotationEvents.cpp
+++ b/src/osgEarthUtil/AnnotationEvents.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/AutoClipPlaneHandler b/src/osgEarthUtil/AutoClipPlaneHandler
index f4f8b35..1f44143 100644
--- a/src/osgEarthUtil/AutoClipPlaneHandler
+++ b/src/osgEarthUtil/AutoClipPlaneHandler
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/AutoClipPlaneHandler.cpp b/src/osgEarthUtil/AutoClipPlaneHandler.cpp
index 07df358..480a191 100644
--- a/src/osgEarthUtil/AutoClipPlaneHandler.cpp
+++ b/src/osgEarthUtil/AutoClipPlaneHandler.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -22,6 +22,7 @@
 #include <osgEarth/Notify>
 #include <osgEarth/Registry>
 #include <osgEarth/Utils>
+#include <osgEarth/CullingUtils>
 
 #define LC "[AutoClip] "
 
@@ -203,7 +204,7 @@ AutoClipPlaneCullCallback::operator()( osg::Node* node, osg::NodeVisitor* nv )
 {
     if ( _active )
     {
-        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( nv );
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
         if ( cv )
         {
             osgEarth::Map* map = _mapNode.valid() ? _mapNode->getMap() : 0;
diff --git a/src/osgEarthUtil/BrightnessContrastColorFilter b/src/osgEarthUtil/BrightnessContrastColorFilter
index 4eb947e..6c3f995 100644
--- a/src/osgEarthUtil/BrightnessContrastColorFilter
+++ b/src/osgEarthUtil/BrightnessContrastColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/BrightnessContrastColorFilter.cpp b/src/osgEarthUtil/BrightnessContrastColorFilter.cpp
index fa8ef57..3e0bf50 100644
--- a/src/osgEarthUtil/BrightnessContrastColorFilter.cpp
+++ b/src/osgEarthUtil/BrightnessContrastColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/CMYKColorFilter b/src/osgEarthUtil/CMYKColorFilter
index 12e70b5..2b28404 100644
--- a/src/osgEarthUtil/CMYKColorFilter
+++ b/src/osgEarthUtil/CMYKColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/CMYKColorFilter.cpp b/src/osgEarthUtil/CMYKColorFilter.cpp
index c7f871c..d5e8047 100644
--- a/src/osgEarthUtil/CMYKColorFilter.cpp
+++ b/src/osgEarthUtil/CMYKColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/CMakeLists.txt b/src/osgEarthUtil/CMakeLists.txt
index 2b7ce10..21a4936 100644
--- a/src/osgEarthUtil/CMakeLists.txt
+++ b/src/osgEarthUtil/CMakeLists.txt
@@ -14,8 +14,8 @@ SET(HEADERS_ROOT
     Common
     Controls
     ClampCallback
+    DataScanner
     EarthManipulator
-    ElevationManager
     ExampleResources
     Export
     FeatureManipTool
@@ -29,7 +29,6 @@ SET(HEADERS_ROOT
     MGRSFormatter
     MGRSGraticule
     MouseCoordsTool
-    ObjectPlacer
     ObjectLocator
     PolyhedralLineOfSight
     RadialLineOfSight
@@ -40,6 +39,7 @@ SET(HEADERS_ROOT
     TFS
     TFSPackager
     TMS
+    TMSBackFiller
     TMSPackager
     UTMGraticule
     WFS
@@ -57,8 +57,8 @@ SET(SOURCES_ROOT
     AutoClipPlaneHandler.cpp
     ClampCallback.cpp
     Controls.cpp
-    EarthManipulator.cpp
-    ElevationManager.cpp
+    DataScanner.cpp
+    EarthManipulator.cpp    
     ExampleResources.cpp
     FeatureManipTool.cpp
     FeatureQueryTool.cpp
@@ -69,7 +69,6 @@ SET(SOURCES_ROOT
     MGRSFormatter.cpp
     MGRSGraticule.cpp
     MouseCoordsTool.cpp
-    ObjectPlacer.cpp
     ObjectLocator.cpp
     PolyhedralLineOfSight.cpp
     RadialLineOfSight.cpp
@@ -79,6 +78,7 @@ SET(SOURCES_ROOT
     TFS.cpp
     TFSPackager.cpp
     TMS.cpp
+    TMSBackFiller.cpp
     TMSPackager.cpp
     UTMGraticule.cpp
     WFS.cpp
@@ -96,6 +96,7 @@ SET(HEADERS_COLORFILTER
     BrightnessContrastColorFilter
     CMYKColorFilter
     GammaColorFilter
+    GLSLColorFilter
     HSLColorFilter
     RGBColorFilter
     ChromaKeyColorFilter
@@ -107,6 +108,7 @@ SET(SOURCES_COLORFILTER
     BrightnessContrastColorFilter.cpp
     CMYKColorFilter.cpp
     GammaColorFilter.cpp
+    GLSLColorFilter.cpp
     HSLColorFilter.cpp
     RGBColorFilter.cpp
     ChromaKeyColorFilter.cpp
diff --git a/src/osgEarthUtil/ChromaKeyColorFilter b/src/osgEarthUtil/ChromaKeyColorFilter
index c18c8c6..2f1a71d 100644
--- a/src/osgEarthUtil/ChromaKeyColorFilter
+++ b/src/osgEarthUtil/ChromaKeyColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ChromaKeyColorFilter.cpp b/src/osgEarthUtil/ChromaKeyColorFilter.cpp
index b088a66..92f7fee 100644
--- a/src/osgEarthUtil/ChromaKeyColorFilter.cpp
+++ b/src/osgEarthUtil/ChromaKeyColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ClampCallback b/src/osgEarthUtil/ClampCallback
index f966f16..a7503f6 100644
--- a/src/osgEarthUtil/ClampCallback
+++ b/src/osgEarthUtil/ClampCallback
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ClampCallback.cpp b/src/osgEarthUtil/ClampCallback.cpp
index d184fa9..5e31c6c 100644
--- a/src/osgEarthUtil/ClampCallback.cpp
+++ b/src/osgEarthUtil/ClampCallback.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/Common b/src/osgEarthUtil/Common
index f44df0f..34b9f92 100644
--- a/src/osgEarthUtil/Common
+++ b/src/osgEarthUtil/Common
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/Controls b/src/osgEarthUtil/Controls
index 1c3482b..07a4f24 100644
--- a/src/osgEarthUtil/Controls
+++ b/src/osgEarthUtil/Controls
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -342,7 +342,7 @@ namespace osgEarth { namespace Util { namespace Controls
         void setText( const std::string& value );
         const std::string& text() const { return _text; }
 
-		void setEncoding( osgText::String::Encoding value );
+        void setEncoding( osgText::String::Encoding value );
         const osgText::String::Encoding& encoding() const { return _encoding; }
 
         void setFont( osgText::Font* font );
@@ -354,6 +354,15 @@ namespace osgEarth { namespace Util { namespace Controls
         void setHaloColor( const osg::Vec4f& value );
         const optional<osg::Vec4f>& haloColor() const { return _haloColor; }
 
+        void setTextBackdropType(osgText::Text::BackdropType value);
+        const osgText::Text::BackdropType getTextBackdropType() const { return _backdropType; }
+
+        void setTextBackdropImplementation(osgText::Text::BackdropImplementation value);
+        const osgText::Text::BackdropImplementation getTextBackdropImplementation() const { return _backdropImpl; }
+
+        void setTextBackdropOffset(float offsetValue);
+        float getTextBackdropOffset() const { return _backdropOffset; }
+
     public: // Control
         virtual void calcSize( const ControlContext& context, osg::Vec2f& out_size );
         virtual void draw    ( const ControlContext& context, DrawableList& out_drawables );
@@ -365,7 +374,37 @@ namespace osgEarth { namespace Util { namespace Controls
         osg::ref_ptr<osgText::Text> _drawable;
         osg::Vec3 _bmin, _bmax;
         optional<osg::Vec4f> _haloColor;
-		osgText::String::Encoding _encoding;
+        osgText::String::Encoding _encoding;
+        osgText::Text::BackdropType _backdropType;
+        osgText::Text::BackdropImplementation _backdropImpl;
+        float _backdropOffset;
+    };
+
+    /**
+     * Button - just a Label with preset background and active colors
+     */
+    class OSGEARTHUTIL_EXPORT ButtonControl : public LabelControl
+    {
+    public:
+        ButtonControl(
+            const std::string&   text        ="",
+            float                fontSize    =18.0f,
+            const osg::Vec4f&    foreColor   =osg::Vec4f(1,1,1,1),
+            const osg::Vec4f&    backColor   =osg::Vec4f(0.5,0.5,0.5,1),
+            const osg::Vec4f&    activeColor =osg::Vec4f(0.5,0.5,1,1),
+            ControlEventHandler* handler     =0L );
+
+        ButtonControl(
+            const std::string&   text,
+            const osg::Vec4f&    foreColor,
+            const osg::Vec4f&    backColor   =osg::Vec4f(0.5,0.5,0.5,1),
+            const osg::Vec4f&    activeColor =osg::Vec4f(0.5,0.5,1,1),
+            float                fontSize    =18.0f,
+            ControlEventHandler* handler     =0L );
+        
+        ButtonControl(
+            const std::string&   text,
+            ControlEventHandler* handler );
     };
 
     /**
@@ -829,7 +868,7 @@ namespace osgEarth { namespace Util { namespace Controls
         bool            _updatePending;
 
         typedef std::map< osg::observer_ptr<osgGA::GUIEventHandler>, osg::observer_ptr<osgViewer::View> > EventHandlersMap;
-		EventHandlersMap _eventHandlersMap;
+        EventHandlersMap _eventHandlersMap;
 
         osg::ref_ptr<ControlNodeBin> _controlNodeBin;
 
diff --git a/src/osgEarthUtil/Controls.cpp b/src/osgEarthUtil/Controls.cpp
index 80c3195..2b73caa 100644
--- a/src/osgEarthUtil/Controls.cpp
+++ b/src/osgEarthUtil/Controls.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -31,6 +31,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/Registry>
 #include <osgEarth/Utils>
+#include <osgEarth/CullingUtils>
 
 using namespace osgEarth;
 using namespace osgEarth::Symbology;
@@ -631,7 +632,10 @@ LabelControl::LabelControl(const std::string& text,
                            const osg::Vec4f&  foreColor):
 _text    ( text ),
 _fontSize( fontSize ),
-_encoding( osgText::String::ENCODING_UNDEFINED )
+_encoding( osgText::String::ENCODING_UNDEFINED ),
+_backdropType( osgText::Text::OUTLINE ),
+_backdropImpl( osgText::Text::STENCIL_BUFFER ),
+_backdropOffset( 0.03f )
 {    
     setFont( Registry::instance()->getDefaultFont() );    
     setForeColor( foreColor );
@@ -643,7 +647,10 @@ LabelControl::LabelControl(const std::string& text,
                            float              fontSize ):
 _text    ( text ),
 _fontSize( fontSize ),
-_encoding( osgText::String::ENCODING_UNDEFINED )
+_encoding( osgText::String::ENCODING_UNDEFINED ),
+_backdropType( osgText::Text::OUTLINE ),
+_backdropImpl( osgText::Text::STENCIL_BUFFER ),
+_backdropOffset( 0.03f )
 {    	
     setFont( Registry::instance()->getDefaultFont() );   
     setForeColor( foreColor );
@@ -654,7 +661,10 @@ LabelControl::LabelControl(Control*           valueControl,
                            float              fontSize,
                            const osg::Vec4f&  foreColor):
 _fontSize( fontSize ),
-_encoding( osgText::String::ENCODING_UNDEFINED )
+_encoding( osgText::String::ENCODING_UNDEFINED ),
+_backdropType( osgText::Text::OUTLINE ),
+_backdropImpl( osgText::Text::STENCIL_BUFFER ),
+_backdropOffset( 0.03f )
 {
     setFont( Registry::instance()->getDefaultFont() );    
     setForeColor( foreColor );
@@ -668,7 +678,10 @@ LabelControl::LabelControl(Control*           valueControl,
                            const osg::Vec4f&  foreColor,
                            float              fontSize ):
 _fontSize( fontSize ),
-_encoding( osgText::String::ENCODING_UNDEFINED )
+_encoding( osgText::String::ENCODING_UNDEFINED ),
+_backdropType( osgText::Text::OUTLINE ),
+_backdropImpl( osgText::Text::STENCIL_BUFFER ),
+_backdropOffset( 0.03f )
 {    	
     setFont( Registry::instance()->getDefaultFont() );   
     setForeColor( foreColor );
@@ -691,10 +704,10 @@ LabelControl::setText( const std::string& value )
 void
 LabelControl::setEncoding( osgText::String::Encoding value )
 {
-	if ( value != _encoding ) {
-		_encoding = value;
-		dirty();
-	}
+    if ( value != _encoding ) {
+        _encoding = value;
+        dirty();
+    }
 }
 
 void
@@ -725,6 +738,33 @@ LabelControl::setHaloColor( const osg::Vec4f& value )
 }
 
 void
+LabelControl::setTextBackdropImplementation(osgText::Text::BackdropImplementation value)
+{
+    if( _backdropImpl != value ) {
+        _backdropImpl = value;
+        dirty();
+    }
+}
+
+void
+LabelControl::setTextBackdropType(osgText::Text::BackdropType value)
+{
+    if( _backdropType != value ) {
+        _backdropType = value;
+        dirty();
+    }
+}
+
+void 
+LabelControl::setTextBackdropOffset(float offsetValue) 
+{
+    if ( offsetValue != _backdropOffset ) {
+        _backdropOffset = offsetValue;
+        dirty();
+    }
+}
+
+void
 LabelControl::calcSize(const ControlContext& cx, osg::Vec2f& out_size)
 {
     if ( visible() == true )
@@ -741,7 +781,7 @@ LabelControl::calcSize(const ControlContext& cx, osg::Vec2f& out_size)
         t->getOrCreateStateSet()->setAttributeAndModes( program, osg::StateAttribute::ON );
 #endif
 
-		t->setText( _text, _encoding );
+        t->setText( _text, _encoding );
         // yes, object coords. screen coords won't work becuase the bounding box will be wrong.
         t->setCharacterSizeMode( osgText::Text::OBJECT_COORDS );
         t->setCharacterSize( _fontSize );
@@ -753,8 +793,9 @@ LabelControl::calcSize(const ControlContext& cx, osg::Vec2f& out_size)
 
         if ( haloColor().isSet() )
         {
-            t->setBackdropType( osgText::Text::OUTLINE );
-            t->setBackdropOffset( 0.03 );
+            t->setBackdropType( _backdropType );
+            t->setBackdropImplementation( _backdropImpl );
+            t->setBackdropOffset( _backdropOffset );
             t->setBackdropColor( haloColor().value() );
         }
 
@@ -776,6 +817,9 @@ LabelControl::calcSize(const ControlContext& cx, osg::Vec2f& out_size)
             (_bmax.x() - _bmin.x()) + padding().x(),
             (_bmax.y() - _bmin.y()) + padding().y() );
 
+    // If width explicitly set and > measured width of label text - use it.
+    if (width().isSet() && width().get() > _renderSize.x()) _renderSize.x() = width().get();
+    
         _drawable = t;
 
         out_size.set(
@@ -808,6 +852,50 @@ LabelControl::draw( const ControlContext& cx, DrawableList& out )
 
 // ---------------------------------------------------------------------------
 
+ButtonControl::ButtonControl(const std::string&   text,
+                             float                fontSize,
+                             const osg::Vec4f&    foreColor,
+                             const osg::Vec4f&    backColor,
+                             const osg::Vec4f&    activeColor,
+                             ControlEventHandler* handler) :
+LabelControl(text, fontSize, foreColor)
+{
+    setBackColor( backColor );
+    setActiveColor( activeColor );
+    setPadding( 6.0f );
+    if ( handler )
+        this->addEventHandler( handler );
+}
+
+ButtonControl::ButtonControl(const std::string&   text,
+                             const osg::Vec4f&    foreColor,
+                             const osg::Vec4f&    backColor,
+                             const osg::Vec4f&    activeColor,
+                             float                fontSize,
+                             ControlEventHandler* handler) :
+LabelControl(text, foreColor, fontSize)
+{
+    setBackColor( backColor );
+    setActiveColor( activeColor );
+    setPadding( 6.0f );
+    if ( handler )
+        this->addEventHandler( handler );
+}
+
+ButtonControl::ButtonControl(const std::string&   text,
+                             ControlEventHandler* handler) :
+LabelControl(text)
+{
+    setForeColor( Color::White );
+    setBackColor( Color::DarkGray );
+    setActiveColor( Color::Blue );
+    setPadding( 6.0f );
+    if ( handler )
+        this->addEventHandler( handler );
+}
+
+// ---------------------------------------------------------------------------
+
 ImageControl::ImageControl( osg::Image* image ) :
 _rotation( 0.0, Units::RADIANS ),
 _fixSizeForRot( false )
@@ -985,19 +1073,19 @@ _min(min),
 _max(max),
 _value(value)
 {
-   //if ( _max <= _min )
-   //    _max = _min+1.0f;
-   //if ( _value < _min )
-   //    _value = _min;
-   //if ( _value > _max )
-   //    _value = _max;
+    //if ( _max <= _min )
+    //    _max = _min+1.0f;
+    //if ( _value < _min )
+    //    _value = _min;
+    //if ( _value > _max )
+    //    _value = _max;
 
-   setHorizFill( true );
-   setVertAlign( ALIGN_CENTER );
-   setHeight( 20.0f );
+    setHorizFill( true );
+    setVertAlign( ALIGN_CENTER );
+    setHeight( 20.0f );
 
-   if ( handler )
-    addEventHandler( handler );
+    if ( handler )
+        addEventHandler( handler );
 }
 
 void
@@ -1316,7 +1404,7 @@ RoundedFrame::draw( const ControlContext& cx, DrawableList& out )
 // ---------------------------------------------------------------------------
 
 Container::Container() :
-_spacing( 1 )
+_spacing( 5.0f )
 {
     //nop
 }
@@ -1659,6 +1747,9 @@ HBox::calcSize(const ControlContext& cx, osg::Vec2f& out_size)
             _renderSize.x() += first ? childSize.x() : childSpacing() + childSize.x();
             _renderSize.y() = osg::maximum( _renderSize.y(), childSize.y() );
         }
+    
+    // If width explicitly set and > total width of children - use it
+    if (width().isSet() && width().get() > _renderSize.x()) _renderSize.x() = width().get();
 
         _renderSize.set(
             _renderSize.x() + padding().x(),
@@ -1718,16 +1809,6 @@ HBox::calcPos(const ControlContext& cx, const osg::Vec2f& cursor, const osg::Vec
 
     osg::Vec2f childCursor = _renderPos;
 
-#if 0
-    // collect all the members
-    for( ControlList::const_iterator i = _controls.begin(); i != _controls.end(); ++i )
-    {
-        Control* child = i->get();
-        child->calcPos( cx, childCursor, _renderSize - padding().size() ); // GW1
-        childCursor.x() += child->margin().left() + child->renderSize().x() + child->margin().right() + childSpacing();
-    }
-#endif
-
     osg::Vec2f renderArea = _renderSize - padding().size();
     for( ControlList::const_iterator i = _controls.begin(); i != _controls.end(); ++i )
     {
@@ -1751,7 +1832,8 @@ HBox::draw( const ControlContext& cx, DrawableList& out )
 
 Grid::Grid()
 {
-    //nop
+    setChildHorizAlign( ALIGN_LEFT );
+    setChildVertAlign( ALIGN_CENTER );
 }
 
 Grid::Grid( const Alignment& halign, const Alignment& valign, const Gutter& padding, float spacing ) :
@@ -2096,7 +2178,7 @@ ControlNode::traverse( osg::NodeVisitor& nv )
     {
         static osg::Vec3d s_zero(0,0,0);
         static osg::Vec4d s_zero_w(0,0,0,1);
-        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( &nv );
+        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
 
         // pull up the per-view data for this view:
         PerViewData& data = _perViewData[cv->getCurrentCamera()->getView()];
@@ -2500,12 +2582,12 @@ ControlCanvas::~ControlCanvas()
     EventHandlersMap::iterator itr;
     for (itr = _eventHandlersMap.begin(); itr != _eventHandlersMap.end(); ++itr)
     {
-		osgGA::GUIEventHandler* pGUIEventHandler = itr->first.get();
-		osgViewer::View* pView = itr->second.get();
-		if ( (pView != NULL) && (pGUIEventHandler != NULL) )
-		{
-			pView->removeEventHandler(pGUIEventHandler);
-		}
+        osgGA::GUIEventHandler* pGUIEventHandler = itr->first.get();
+        osgViewer::View* pView = itr->second.get();
+        if ( (pView != NULL) && (pGUIEventHandler != NULL) )
+        {
+            pView->removeEventHandler(pGUIEventHandler);
+        }
     }
 }
 
@@ -2558,17 +2640,17 @@ ControlCanvas::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter
     if ( !_context._vp )
         return false;
 
-	for (ControlList::reverse_iterator i = _controls.rbegin(); i != _controls.rend(); ++i)
-	{
-		Control* control = i->get();
-		if (control->isDirty())
-		{
-			aa.requestRedraw();
-			break;
-		}
-	}
-
-	bool handled = false;
+    for (ControlList::reverse_iterator i = _controls.rbegin(); i != _controls.rend(); ++i)
+    {
+        Control* control = i->get();
+        if (control->isDirty())
+        {
+            aa.requestRedraw();
+            break;
+        }
+    }
+
+    bool handled = false;
     //Send a frame event to all controls
     if ( ea.getEventType() == osgGA::GUIEventAdapter::FRAME )
     {
diff --git a/src/osgEarthUtil/ShadowUtils b/src/osgEarthUtil/DataScanner
similarity index 56%
copy from src/osgEarthUtil/ShadowUtils
copy to src/osgEarthUtil/DataScanner
index efc9446..70dcfcb 100644
--- a/src/osgEarthUtil/ShadowUtils
+++ b/src/osgEarthUtil/DataScanner
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -16,22 +16,31 @@
 * 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_SHADOW_UTILS_H
-#define OSGEARTH_SHADOW_UTILS_H  1
+#ifndef OSGEARTHUTIL_DATA_SCANNER_H
+#define OSGEARTHUTIL_DATA_SCANNER_H
 
 #include <osgEarthUtil/Common>
-#include <osg/Group>
-#include <osgShadow/ShadowedScene>
+#include <osgEarth/ImageLayer>
+#include <string>
 
-namespace osgEarth { namespace Util 
+namespace osgEarth { namespace Util
 {
-    struct OSGEARTHUTIL_EXPORT ShadowUtils
+    /**
+     * Scans local directories in search of image and elevation data.
+     */
+    class OSGEARTHUTIL_EXPORT DataScanner
     {
-        static bool setUpShadows(
-            osgShadow::ShadowedScene* sscene,
-            osg::Group*               root);
+    public:
+        DataScanner() { }
+        virtual ~DataScanner() { }
+
+    public:
+        void findImageLayers(
+            const std::string&              absRootPath,
+            const std::vector<std::string>& extensions,
+            osgEarth::ImageLayerVector&     out_imageLayers) const;
     };
 
 } } // namespace osgEarth::Util
 
-#endif // OSGEARTH_SHADOW_UTILS_H
+#endif // OSGEARTHUTIL_DATA_SCANNER_H
diff --git a/src/osgEarthUtil/DataScanner.cpp b/src/osgEarthUtil/DataScanner.cpp
new file mode 100644
index 0000000..875d1be
--- /dev/null
+++ b/src/osgEarthUtil/DataScanner.cpp
@@ -0,0 +1,77 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osgEarthUtil/DataScanner>
+#include <osgEarthDrivers/gdal/GDALOptions>
+#include <osgDB/FileUtils>
+#include <osgDB/FileNameUtils>
+
+#define LC "[DataScanner] "
+
+using namespace osgEarth;
+using namespace osgEarth::Util;
+using namespace osgEarth::Drivers;
+
+namespace
+{
+    void traverse(const std::string&              path,
+                  const std::vector<std::string>& extensions,
+                  ImageLayerVector&               out_imageLayers)
+    {
+        if ( osgDB::fileType(path) == osgDB::DIRECTORY )
+        {
+            osgDB::DirectoryContents files = osgDB::getDirectoryContents(path);
+            for( osgDB::DirectoryContents::const_iterator f = files.begin(); f != files.end(); ++f )
+            {
+                if ( f->compare(".") == 0 || f->compare("..") == 0 )
+                    continue;
+
+                std::string filepath = osgDB::concatPaths( path, *f );
+                traverse( filepath, extensions, out_imageLayers );
+            }
+        }
+
+        else if ( osgDB::fileType(path) == osgDB::REGULAR_FILE )
+        {
+            const std::string ext = osgDB::getLowerCaseFileExtension(path);
+
+            if ( std::find(extensions.begin(), extensions.end(), ext) != extensions.end() )
+            {
+                GDALOptions gdal;
+                gdal.url() = path;
+                //gdal.interpolation() = INTERP_NEAREST;
+
+                ImageLayerOptions options( path, gdal );
+                options.cachePolicy() = CachePolicy::NO_CACHE;
+
+                ImageLayer* layer = new ImageLayer(options);
+                out_imageLayers.push_back( layer );
+                OE_INFO << LC << "Found " << path << std::endl;
+            }
+        }
+    }
+}
+
+
+void
+DataScanner::findImageLayers(const std::string&              absRootPath,
+                             const std::vector<std::string>& extensions,
+                             ImageLayerVector&               out_imageLayers) const
+{
+    traverse( absRootPath, extensions, out_imageLayers );
+}
diff --git a/src/osgEarthUtil/EarthManipulator b/src/osgEarthUtil/EarthManipulator
index 0b7b92e..0f30f10 100644
--- a/src/osgEarthUtil/EarthManipulator
+++ b/src/osgEarthUtil/EarthManipulator
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -74,11 +74,14 @@ namespace osgEarth { namespace Util
 
         /** Bindable event types. */
         enum EventType {
-            EVENT_MOUSE_CLICK        = osgGA::GUIEventAdapter::USER << 1,
             EVENT_MOUSE_DOUBLE_CLICK = osgGA::GUIEventAdapter::DOUBLECLICK,
             EVENT_MOUSE_DRAG         = osgGA::GUIEventAdapter::DRAG,
             EVENT_KEY_DOWN           = osgGA::GUIEventAdapter::KEYDOWN,
-            EVENT_SCROLL             = osgGA::GUIEventAdapter::SCROLL
+            EVENT_SCROLL             = osgGA::GUIEventAdapter::SCROLL,
+            EVENT_MOUSE_CLICK        = osgGA::GUIEventAdapter::USER << 1,
+            EVENT_MULTI_DRAG         = osgGA::GUIEventAdapter::USER << 2,   // drag with 2 fingers
+            EVENT_MULTI_PINCH        = osgGA::GUIEventAdapter::USER << 3,   // pinch with 2 fingers
+            EVENT_MULTI_TWIST        = osgGA::GUIEventAdapter::USER << 4    // drag 2 fingers in different directions
         };
 
         /** Bindable mouse buttons. */
@@ -325,6 +328,13 @@ namespace osgEarth { namespace Util
                 int modkey_mask =0L,
                 const ActionOptions& options =ActionOptions() );
 
+
+            void bindPinch(
+                ActionType action, const ActionOptions& =ActionOptions() );
+   
+            void bindMultiDrag(
+                ActionType action, const ActionOptions& =ActionOptions() );
+
             /**
              * Sets an overall mouse sensitivity factor.
              *
@@ -691,17 +701,6 @@ namespace osgEarth { namespace Util
         // the "home" function at the moment -- look into depcrecation -GW
         void setByLookAt(const osg::Vec3d& eye, const osg::Vec3d& lv, const osg::Vec3d& up);
 
-        // Applies an action using the raw input parameters.
-        bool handleAction( const Action& action, double dx, double dy, double duration );
-
-        virtual bool handleMouseAction( const Action& action, osg::View* view );
-        virtual bool handleMouseClickAction( const Action& action );
-        virtual bool handleKeyboardAction( const Action& action, double duration_s = DBL_MAX );
-        virtual bool handleScrollAction( const Action& action, double duration_s = DBL_MAX );
-        virtual bool handlePointAction( const Action& type, float mx, float my, osg::View* view );
-        virtual void handleContinuousAction( const Action& action, osg::View* view );
-        virtual void handleMovementAction( const ActionType& type, double dx, double dy, osg::View* view );
-
         // checks to see whether the mouse is "moving".
         bool isMouseMoving();
 
@@ -778,6 +777,41 @@ namespace osgEarth { namespace Util
         const GeoPoint& centerMap() const { return _centerMap; }
 
     protected:
+        typedef osgGA::GUIEventAdapter::TouchData::TouchPoint TouchPoint;
+        typedef std::vector<TouchPoint> MultiTouchPoint; // one per ID (finger/touchpoint)
+        typedef std::deque<MultiTouchPoint> MultiTouchPointQueue;
+        MultiTouchPointQueue _touchPointQueue;
+        //struct TouchEvent {
+        //    EventType _eventType;     // derived touch/other event
+        //    float     _deltaDistance; // change in distance between 2 touches
+        //    float     _dx[2], _dy[2]; // movement of primary and secondary touch points
+        //    float     _dot;
+        //};
+        struct TouchEvent {
+            TouchEvent() : _mbmask(0) { }
+            EventType _eventType;
+            unsigned  _mbmask;
+            float     _dx, _dy;
+        };
+        typedef std::vector<TouchEvent> TouchEvents;
+        void addTouchEvents( const osgGA::GUIEventAdapter& ea );
+        bool parseTouchEvents( TouchEvents& ev );
+
+
+        // Applies an action using the raw input parameters.
+        bool handleAction( const Action& action, double dx, double dy, double duration );
+
+        virtual bool handleMouseAction( const Action& action, osg::View* view );
+        virtual bool handleMouseClickAction( const Action& action );
+        virtual bool handleKeyboardAction( const Action& action, double duration_s = DBL_MAX );
+        virtual bool handleScrollAction( const Action& action, double duration_s = DBL_MAX );
+        virtual bool handlePointAction( const Action& type, float mx, float my, osg::View* view );
+        virtual void handleContinuousAction( const Action& action, osg::View* view );
+        virtual void handleMovementAction( const ActionType& type, double dx, double dy, osg::View* view );
+        //virtual bool handleMultiTouchAction( const Action& action, const TouchEvent& te, osg::View* view );
+
+    protected:
+
         // makeshift "stack" of the last 2 incoming events.
         osg::ref_ptr<const osgGA::GUIEventAdapter> _ga_t1;
         osg::ref_ptr<const osgGA::GUIEventAdapter> _ga_t0;
diff --git a/src/osgEarthUtil/EarthManipulator.cpp b/src/osgEarthUtil/EarthManipulator.cpp
index 92239e7..17f96ff 100644
--- a/src/osgEarthUtil/EarthManipulator.cpp
+++ b/src/osgEarthUtil/EarthManipulator.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -232,7 +232,8 @@ _auto_vp_duration( rhs._auto_vp_duration ),
 _min_vp_duration_s( rhs._min_vp_duration_s ),
 _max_vp_duration_s( rhs._max_vp_duration_s ),
 _camProjType( rhs._camProjType ),
-_camFrustOffsets( rhs._camFrustOffsets )
+_camFrustOffsets( rhs._camFrustOffsets ),
+_breakTetherActions( rhs._breakTetherActions )
 {
     //NOP
 }
@@ -337,6 +338,23 @@ EarthManipulator::Settings::bindScroll(ActionType action, int scrolling_motion,
         Action( action, options ) );
 }
 
+
+void
+EarthManipulator::Settings::bindPinch(ActionType action, const ActionOptions& options)
+{
+    bind(
+        InputSpec( EarthManipulator::EVENT_MULTI_PINCH, 0, 0 ),
+        Action( action, options ) );
+}
+
+void
+EarthManipulator::Settings::bindMultiDrag(ActionType action, const ActionOptions& options)
+{
+    bind(
+        InputSpec( EarthManipulator::EVENT_MULTI_DRAG, 0, 0 ),
+        Action( action, options ) );
+}
+
 const EarthManipulator::Action&
 EarthManipulator::Settings::getAction(int event_type, int input_mask, int modkey_mask) const
 {
@@ -487,6 +505,13 @@ EarthManipulator::configureDefaultSettings()
     _settings->bindMouseDoubleClick( ACTION_GOTO, osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON, 0L, options );
     _settings->bindMouseDoubleClick( ACTION_GOTO, osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON, osgGA::GUIEventAdapter::MODKEY_CTRL, options );
 
+    // map multi-touch pinch to a discrete zoom
+    options.clear();
+    _settings->bindPinch( ACTION_ZOOM, options );
+
+    options.clear();
+    _settings->bindMultiDrag( ACTION_ROTATE, options );
+
     //_settings->setThrowingEnabled( false );
     _settings->setLockAzimuthWhilePanning( true );
 }
@@ -876,9 +901,6 @@ EarthManipulator::setViewpoint( const Viewpoint& vp, double duration_s )
         
         _thrown = false;
         _task->_type = TASK_NONE;
-
-        // recalculate the center point.
-        recalculateCenter();
     }
     else
     {
@@ -938,8 +960,6 @@ EarthManipulator::setViewpoint( const Viewpoint& vp, double duration_s )
         osg::Matrix new_rot = osg::Matrixd( azim_q * pitch_q );
 
         _rotation = osg::Matrixd::inverse(new_rot).getRotate();
-
-        recalculateCenter();
     }
 }
 
@@ -1380,6 +1400,7 @@ EarthManipulator::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapt
         return false;
     }
 
+
     // the camera manipulator runs last after any other event handlers. So bail out
     // if the incoming event has already been handled by another handler.
     if ( ea.getHandled() )
@@ -1406,122 +1427,157 @@ EarthManipulator::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapt
         }
     }
 
-    switch( ea.getEventType() )
+
+    if ( ea.isMultiTouchEvent() )
     {
-        case osgGA::GUIEventAdapter::PUSH:
-            resetMouse( aa );
-            addMouseEvent( ea );
-            _mouse_down_event = &ea;
-            aa.requestRedraw();
-            handled = true;
-            break;       
-        
-        case osgGA::GUIEventAdapter::RELEASE:
+        // not a mouse event; clear the mouse queue.
+        resetMouse( aa );
 
-            if ( _continuous )
+        // queue up a touch event set and figure out the current state:
+        addTouchEvents(ea);
+        TouchEvents te;
+        if ( parseTouchEvents(te) )
+        {
+            for( TouchEvents::iterator i = te.begin(); i != te.end(); ++i )
             {
-                // bail out of continuous mode if necessary:
-                _continuous = false;
-                aa.requestContinuousUpdate( false );
+                //OE_WARN << LC << "P: " << i->_dx << ", " << i->_dy << std::endl;
+                Action action = _settings->getAction(i->_eventType, i->_mbmask, 0);
+
+                // here we adjust for action scale, global sensitivy
+                double dx = i->_dx, dy = i->_dy;
+                dx *= _settings->getMouseSensitivity();
+                dy *= _settings->getMouseSensitivity();
+                applyOptionsToDeltas( action, dx, dy );
+
+                handleMovementAction(action._type, dx, dy, view);
+                aa.requestRedraw();
             }
-            else
-            {
-#if 0 // disabled - not implemented
-                // check for a mouse-throw continuation:
-                if ( _settings->getThrowingEnabled() && isMouseMoving() )
+            handled = true;
+        }
+    }
+
+    if ( !handled )
+    {
+        // not a touch event; clear the touch queue.
+        //_touchPointQueue.clear();
+
+        switch( ea.getEventType() )
+        {
+            case osgGA::GUIEventAdapter::PUSH:
+                resetMouse( aa );
+                addMouseEvent( ea );
+                _mouse_down_event = &ea;
+                aa.requestRedraw();
+                handled = true;
+                break;       
+            
+            case osgGA::GUIEventAdapter::RELEASE:
+
+                if ( _continuous )
                 {
-                    action = _last_action;
-                    if( handleMouseAction( action, aa.asView() ) )
-                    {
-                        aa.requestRedraw();
-                        aa.requestContinuousUpdate( true );
-                        _thrown = true;
-                    }
+                    // bail out of continuous mode if necessary:
+                    _continuous = false;
+                    aa.requestContinuousUpdate( false );
                 }
-                else 
-#endif
-                if ( isMouseClick( &ea ) )
+                else
                 {
-                    addMouseEvent( ea );
-                    if ( _mouse_down_event )
+    #if 0 // disabled - not implemented
+                    // check for a mouse-throw continuation:
+                    if ( _settings->getThrowingEnabled() && isMouseMoving() )
                     {
-                        action = _settings->getAction( EVENT_MOUSE_CLICK, _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() );
-                        if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ))
-                            aa.requestRedraw();                
+                        action = _last_action;
+                        if( handleMouseAction( action, aa.asView() ) )
+                        {
+                            aa.requestRedraw();
+                            aa.requestContinuousUpdate( true );
+                            _thrown = true;
+                        }
+                    }
+                    else 
+    #endif
+                    if ( isMouseClick( &ea ) )
+                    {
+                        addMouseEvent( ea );
+                        if ( _mouse_down_event )
+                        {
+                            action = _settings->getAction( EVENT_MOUSE_CLICK, _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() );
+                            if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ))
+                                aa.requestRedraw();
+                        }
+                        resetMouse( aa );
+                    }
+                    else
+                    {
+                        resetMouse( aa );
+                        addMouseEvent( ea );
                     }
-                    resetMouse( aa );
                 }
-                else
+                handled = true;
+                break;
+                
+            case osgGA::GUIEventAdapter::DOUBLECLICK:
+                // bail out of continuous mode if necessary:
+                _continuous = false;
+                addMouseEvent( ea );
+                if (_mouse_down_event)
                 {
+                    action = _settings->getAction( ea.getEventType(), _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() );
+                    if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ) )
+                        aa.requestRedraw();
                     resetMouse( aa );
-                    addMouseEvent( ea );
+                    handled = true;
                 }
-            }
-            handled = true;
-            break;
-            
-        case osgGA::GUIEventAdapter::DOUBLECLICK:
-            // bail out of continuous mode if necessary:
-            _continuous = false;
-            addMouseEvent( ea );
-            if (_mouse_down_event)
-            {
-                action = _settings->getAction( ea.getEventType(), _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() );
-                if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ) )
-                    aa.requestRedraw();
-                resetMouse( aa );
-                handled = true;
-            }
-            break;
+                break;
 
-        case osgGA::GUIEventAdapter::MOVE: // MOVE not currently bindable
-            //NOP
-            break;
+            case osgGA::GUIEventAdapter::MOVE: // MOVE not currently bindable
+                //NOP
+                break;
 
-        case osgGA::GUIEventAdapter::DRAG:
-            {
-                action = _settings->getAction( ea.getEventType(), ea.getButtonMask(), ea.getModKeyMask() );
-                addMouseEvent( ea );
-                bool wasContinuous = _continuous;
-                _continuous = action.getBoolOption(OPTION_CONTINUOUS, false);
-                if ( handleMouseAction( action, aa.asView() ) )
-                    aa.requestRedraw();
+            case osgGA::GUIEventAdapter::DRAG:
+                {
+                    action = _settings->getAction( ea.getEventType(), ea.getButtonMask(), ea.getModKeyMask() );
+                    addMouseEvent( ea );
+                    bool wasContinuous = _continuous;
+                    _continuous = action.getBoolOption(OPTION_CONTINUOUS, false);
+                    if ( handleMouseAction( action, aa.asView() ) )
+                        aa.requestRedraw();
 
-                if ( _continuous && !wasContinuous )
-                    _last_continuous_action_time = _time_s_now;
+                    if ( _continuous && !wasContinuous )
+                        _last_continuous_action_time = _time_s_now;
 
-                aa.requestContinuousUpdate(_continuous);
-                _thrown = false;
+                    aa.requestContinuousUpdate(_continuous);
+                    _thrown = false;
+                    handled = true;
+                }
+                break;
+
+            case osgGA::GUIEventAdapter::KEYDOWN:
+                if ( ea.getKey() < osgGA::GUIEventAdapter::KEY_Shift_L )
+                {
+                    resetMouse( aa );
+                    action = _settings->getAction( ea.getEventType(), ea.getKey(), ea.getModKeyMask() );
+                    if ( handleKeyboardAction( action ) )
+                        aa.requestRedraw();
+                    handled = true;
+                }
+                break;
+                
+            case osgGA::GUIEventAdapter::KEYUP:
+                resetMouse( aa );
+                _task->_type = TASK_NONE;
                 handled = true;
-            }
-            break;
+                break;
 
-        case osgGA::GUIEventAdapter::KEYDOWN:
-            if ( ea.getKey() < osgGA::GUIEventAdapter::KEY_Shift_L )
-            {
+            case osgGA::GUIEventAdapter::SCROLL:
                 resetMouse( aa );
-                action = _settings->getAction( ea.getEventType(), ea.getKey(), ea.getModKeyMask() );
-                if ( handleKeyboardAction( action ) )
+                addMouseEvent( ea );
+                action = _settings->getAction( ea.getEventType(), ea.getScrollingMotion(), ea.getModKeyMask() );
+                if ( handleScrollAction( action, 0.2 ) )
                     aa.requestRedraw();
                 handled = true;
-            }
-            break;
-            
-        case osgGA::GUIEventAdapter::KEYUP:
-            resetMouse( aa );
-            _task->_type = TASK_NONE;
-            handled = true;
-            break;
-
-        case osgGA::GUIEventAdapter::SCROLL:
-            resetMouse( aa );
-            addMouseEvent( ea );
-            action = _settings->getAction( ea.getEventType(), ea.getScrollingMotion(), ea.getModKeyMask() );
-            if ( handleScrollAction( action, 0.2 ) )
-                aa.requestRedraw();
-            handled = true;
-            break;
-        default: break;
+                break;
+            default: break;
+        }
     }
 
     // if a new task was started, request continuous updates.
@@ -1670,6 +1726,7 @@ EarthManipulator::flushMouseEventStack()
 {
     _ga_t1 = NULL;
     _ga_t0 = NULL;
+    //_touchPointQueue.clear();
 }
 
 
@@ -1678,6 +1735,115 @@ EarthManipulator::addMouseEvent(const osgGA::GUIEventAdapter& ea)
 {
     _ga_t1 = _ga_t0;
     _ga_t0 = &ea;
+    //_touchPointQueue.clear();
+}
+
+void
+EarthManipulator::addTouchEvents(const osgGA::GUIEventAdapter& ea)
+{
+    // first, push the old event to the back of the queue.
+    while ( _touchPointQueue.size() > 1 )
+        _touchPointQueue.pop_front();
+
+    // queue any new events.
+    if ( ea.isMultiTouchEvent() )
+    {
+        osgGA::GUIEventAdapter::TouchData* data = ea.getTouchData();
+
+        _touchPointQueue.push_back(MultiTouchPoint());
+        MultiTouchPoint& ev = _touchPointQueue.back();
+
+        for( unsigned i=0; i<data->getNumTouchPoints(); ++i )
+        {
+            osgGA::GUIEventAdapter::TouchData::TouchPoint tp = data->get(i);
+            ev.resize(tp.id+1);
+            ev[tp.id] = tp; // overwrites duplicates automatically.
+        }
+    }
+}
+
+bool
+EarthManipulator::parseTouchEvents( TouchEvents& output )
+{
+    const float sens = 0.005f;
+
+    // two-finger drag gestures:
+    if (_touchPointQueue.size() == 2 )
+    {
+        if (_touchPointQueue[0].size()   == 2 &&     // two fingers
+            _touchPointQueue[1].size()   == 2)       // two fingers
+        {
+            MultiTouchPoint& p0 = _touchPointQueue[0];
+            MultiTouchPoint& p1 = _touchPointQueue[1];
+
+            if (p0[0].phase != osgGA::GUIEventAdapter::TOUCH_ENDED &&
+                p1[0].phase != osgGA::GUIEventAdapter::TOUCH_ENDED &&
+                p0[1].phase == osgGA::GUIEventAdapter::TOUCH_MOVED &&
+                p1[1].phase == osgGA::GUIEventAdapter::TOUCH_MOVED)
+            {
+                // gather information about what happened:
+                float dx[2], dy[2];
+                for( int i=0; i<2; ++i )
+                {
+                    dx[i] = p1[i].x - p0[i].x;
+                    dy[i] = p1[i].y - p0[i].y;
+                }
+                osg::Vec2f vec0 = osg::Vec2f(p0[1].x,p0[1].y)-osg::Vec2f(p0[0].x,p0[0].y);
+                osg::Vec2f vec1 = osg::Vec2f(p1[1].x,p1[1].y)-osg::Vec2f(p1[0].x,p1[0].y);
+                float deltaDistance = vec1.length() - vec0.length();
+
+                vec0.normalize();
+                vec1.normalize();
+                float dot = fabs( vec0 * vec1 );
+
+                // how see if that corresponds to any touch events:
+                {
+                    // distance between the fingers changed: a pinch.
+                    output.push_back(TouchEvent());
+                    TouchEvent& ev = output.back();
+                    ev._eventType = EVENT_MULTI_PINCH;
+                    ev._dx = 0.0, ev._dy = deltaDistance * -sens;
+                }
+
+                {
+                    // angle between vectors changed: a twist.
+                    output.push_back(TouchEvent());
+                    TouchEvent& ev = output.back();
+                    ev._eventType = EVENT_MULTI_TWIST;
+                    ev._dx = 0.0, ev._dy = dot * sens;
+                }
+
+                {
+                    // two-finger drag.
+                    output.push_back(TouchEvent());
+                    TouchEvent& ev = output.back();
+                    ev._eventType = EVENT_MULTI_DRAG;
+                    ev._dx = 0.5 * (dx[0]+dx[1]) * sens;
+                    ev._dy = 0.5 * (dy[0]+dy[1]) * sens;
+                }
+            }
+        }
+
+        else if (_touchPointQueue[0].size() >= 1 &&     // one finger
+                 _touchPointQueue[1].size() >= 1)       // one finger
+        {
+            MultiTouchPoint& p0 = _touchPointQueue[0];
+            MultiTouchPoint& p1 = _touchPointQueue[1];
+
+            if (p0[0].phase != osgGA::GUIEventAdapter::TOUCH_ENDED &&
+                p1[0].phase == osgGA::GUIEventAdapter::TOUCH_MOVED )
+            {
+                output.push_back(TouchEvent());
+                TouchEvent& ev = output.back();
+                ev._eventType = EVENT_MOUSE_DRAG;
+                ev._mbmask = osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON;
+                ev._dx = (p1[0].x - p0[0].x) * sens;
+                ev._dy = (p1[0].y - p0[0].y) * sens;
+            }
+        }
+    }
+
+    return output.size() > 0;
 }
 
 void
@@ -2145,7 +2311,6 @@ EarthManipulator::handleMovementAction( const ActionType& type, double dx, doubl
 {
     switch( type )
     {
-    default:break;
     case ACTION_PAN:
         pan( dx, dy );
         break;
@@ -2169,6 +2334,7 @@ EarthManipulator::handleMovementAction( const ActionType& type, double dx, doubl
     case ACTION_EARTH_DRAG:
         drag( dx, dy, view );
         break;
+    default:break;
     }
 }
 
@@ -2325,6 +2491,22 @@ EarthManipulator::handleScrollAction( const Action& action, double duration )
     return handleAction( action, dx, dy, duration );
 }
 
+#if 0
+bool
+EarthManipulator::handleMultiTouchAction( const Action& action, const EarthManipulator::TouchEvent& te, osg::View* view )
+{
+    if ( action._type == ACTION_ZOOM )
+    {
+        handleMovementAction( action._type, 0.0, te._deltaDistance, view );
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+#endif
+
 bool
 EarthManipulator::handleAction( const Action& action, double dx, double dy, double duration )
 {
diff --git a/src/osgEarthUtil/ElevationManager b/src/osgEarthUtil/ElevationManager
deleted file mode 100644
index 4829f99..0000000
--- a/src/osgEarthUtil/ElevationManager
+++ /dev/null
@@ -1,191 +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 OSGEARTHUTIL_ELEVATION_MANAGER_H
-#define OSGEARTHUTIL_ELEVATION_MANAGER_H
-
-#include <osgEarthUtil/Common>
-#include <osgEarth/MapFrame>
-
-namespace osgEarth { namespace Util
-{
-    using namespace osgEarth;
-
-    /**
-     * ElevationManager (EM) lets you query the elevation at any point on a terrain map.
-     * 
-     * Rather than intersecting with a loaded scene graph, EM uses the osgEarth
-     * engine to directly access the best terrain tile for elevation query. You
-     * give it the DEM resolution at which you want an elevation point, and it will
-     * access the necessary tile and sample it.
-     *
-     * EM supports two types of sampling:
-     *
-     * PARAMTERIC - EM will sample the actual heightfield directly. This method is the
-     *   fastest since it does not require geometry intersection testing.
-     * GEOMETRIC - EM will create a temporary tesselated terrain tile and do an 
-     *   intersection test (using osgUtil::IntersectionVisitor). This method is slower
-     *   but more visually correlated.
-     *
-     * NOTE: EM does NOT take into account rendering properties like vertical scale or
-     * skirts. If you need a vertical scale, for example, simply scale the resulting
-     * elevation value.
-     */
-    class OSGEARTHUTIL_EXPORT ElevationManager : public osg::Referenced
-    {
-    public:
-        /** Technique for elevation data sampling - see setTechnique */
-        enum Technique
-        {
-            /** Intersect with triangulated geometry - using the highest resolution
-                data available from the Map source layers */
-            TECHNIQUE_GEOMETRIC,
-
-            /** Sample height from the parametric heightfield directly (bilinear) */
-            TECHNIQUE_PARAMETRIC
-        };
-
-    public:
-        /**
-         * Constructs a new elevation manager. If you are not using a MapNode,
-         * use this constructor to perform elevation queries against a Map. If
-         * you *do* have a MapNode, use the other CTOR that takes a MapNode.
-         *
-         * @param map
-         *      Map against which to perform elevation queries.
-         * @param technique
-         *      Technique to use for elevation data sampling.
-         */
-        ElevationManager( Map* map );
-
-        /** dtor */
-        virtual ~ElevationManager() { }
-
-        /**
-         * Gets the terrain elevation at a point, given a terrain resolution.
-         *
-         * @param x, y
-         *      Map coordinates for which to query elevation.
-         * @param resolution
-         *      Optimal resolution of elevation data to use for the query (if available).
-         *      Pass in 0 (zero) to use the best available resolution.
-         * @param srs
-         *      Spatial reference of x, y, and resolution. If this is NULL, assume that 
-         *      the input values are expressed in terms of the Map's SRS.
-         * @param out_elevation
-         *      Resulting elevation value (if the method returns true.)
-         * @param out_resolution
-         *      Resolution of the resulting elevation value (if the method returns true).
-         * 
-         * @return True if the query succeeded, false upon failure.
-         */
-        bool getElevation(
-            double x, double y,
-            double resolution,
-            const SpatialReference* srs,
-            double& out_elevation,
-            double& out_resolution);
-
-        /**
-         * Gets a matrix that you can use to position a node at the specified coordinates.
-         * The elevation of the object will be the ground height + the specified Z value.
-         * The ground height will be determined by using getElevation() with the 
-         * specified resolution.
-         *
-         * Returns TRUE if the output matrix is valid.
-         */
-        bool getPlacementMatrix(
-            double x, double y, double z,
-            double resolution,
-            const SpatialReference* srs,
-            osg::Matrixd& out_matrix,
-            double& out_elevation,
-            double& out_resolution);
-
-        /**
-         * Sets the technique to use for height determination. See the Technique
-         * enum in this class. The default is TECHNIQUE_PARAMETRIC.
-         */
-        void setTechnique( Technique technique );
-
-        /**
-         * Gets the technique to use for height determination. See the Technique
-         * enum in this class.
-         */
-        Technique getTechnique() const;
-
-        /**
-         * Sets the maximum cache size for elevation tiles.
-         */
-        void setMaxTilesToCache( int value );
-
-        /**
-         * Gets the maximum cache size for elevation tiles.
-         */
-        int getMaxTilesToCache() const;
-
-        /**
-         * Sets the elevation interpolation to use when sampling data
-         */
-        void setInterpolation( ElevationInterpolation interp );
-
-        /**
-         * Gets the elevation interpolation to use when sampling data
-         */
-        ElevationInterpolation getElevationInterpolation() const;
-
-        /**
-        * Sets the maximum level override for elevation queries.
-        * A value of -1 turns off the override.
-        */
-        void setMaxLevelOverride(int maxLevelOverride);
-
-        /**
-        * Gets the maximum level override for elevation queries.
-        */
-        int getMaxLevelOverride() const;
-
-    private:
-        MapFrame _mapf;
-        typedef std::map< osgTerrain::TileID, osg::ref_ptr<osgTerrain::TerrainTile> > TileTable;
-        TileTable _tileCache;
-        typedef std::list< osgTerrain::TileID > TileIdList;
-        TileIdList _tileCacheFIFO;
-        int _maxCacheSize;
-        int _tileSize;
-        unsigned int _maxDataLevel;
-        int _maxLevelOverride;
-        Technique _technique;
-        ElevationInterpolation _interpolation;
-
-    private:
-        void postCTOR();
-        void sync();
-
-        bool getElevationImpl(
-            double x, double y,
-            double resolution,
-            const SpatialReference* srs,
-            double& out_elevation,
-            double& out_resolution);
-    };
-
-} } // namespace osgEarth::Util
-
-#endif // OSGEARTHUTIL_ELEVATION_MANAGER_H
-
diff --git a/src/osgEarthUtil/ElevationManager.cpp b/src/osgEarthUtil/ElevationManager.cpp
deleted file mode 100644
index 207a43f..0000000
--- a/src/osgEarthUtil/ElevationManager.cpp
+++ /dev/null
@@ -1,344 +0,0 @@
-#include <osgEarthUtil/ElevationManager>
-#include <osgEarth/Locators>
-#include <osgEarth/HeightFieldUtils>
-#include <osgTerrain/TerrainTile>
-#include <osgTerrain/GeometryTechnique>
-#include <osgUtil/IntersectionVisitor>
-#include <osgUtil/LineSegmentIntersector>
-
-#define LC "[ElevationManager] "
-
-using namespace osgEarth;
-using namespace osgEarth::Util;
-using namespace OpenThreads;
-
-ElevationManager::ElevationManager( Map* map ) :
-_mapf( map, Map::ELEVATION_LAYERS )
-{
-    postCTOR();
-}
-
-void
-ElevationManager::postCTOR()
-{
-    _tileSize = 0;
-    _maxDataLevel = 0;
-    _maxCacheSize = 100;
-    _technique = TECHNIQUE_PARAMETRIC;
-    _interpolation = INTERP_BILINEAR;
-    _maxLevelOverride = -1;
-}
-
-void
-ElevationManager::sync()
-{
-    if ( _mapf.sync() || _tileSize == 0 || _maxDataLevel == 0 )
-    {
-        _tileSize = 0;
-        _maxDataLevel = 0;
-
-        for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i )
-        {
-            // we need the maximum tile size
-            int layerTileSize = i->get()->getTileSize();
-            if ( layerTileSize > _tileSize )
-                _tileSize = layerTileSize;
-
-            // we also need the maximum available data level.
-            unsigned int layerMaxDataLevel = i->get()->getMaxDataLevel();
-            if ( layerMaxDataLevel > _maxDataLevel )
-                _maxDataLevel = layerMaxDataLevel;
-        }
-    }
-}
-
-ElevationManager::Technique
-ElevationManager::getTechnique() const
-{
-    return _technique;
-}
-
-void
-ElevationManager::setTechnique( ElevationManager::Technique technique )
-{
-    _technique = technique;
-}
-
-void
-ElevationManager::setMaxTilesToCache( int value )
-{
-    _maxCacheSize = value;
-}
-
-int
-ElevationManager::getMaxTilesToCache() const
-{
-    return _maxCacheSize;
-}
-
-void
-ElevationManager::setMaxLevelOverride(int maxLevelOverride)
-{
-    _maxLevelOverride = maxLevelOverride;
-}
-
-int
-ElevationManager::getMaxLevelOverride() const
-{
-    return _maxLevelOverride;
-}
-
-void
-ElevationManager::setInterpolation( ElevationInterpolation interp)
-{
-    _interpolation = interp;
-}
-
-ElevationInterpolation
-ElevationManager::getElevationInterpolation() const
-{
-    return _interpolation;
-}
-
-bool
-ElevationManager::getElevation(double x, double y,
-                               double resolution,
-                               const SpatialReference* srs,
-                               double& out_elevation,
-                               double& out_resolution)
-{
-    sync();
-    return getElevationImpl(x, y, resolution, srs, out_elevation, out_resolution);
-}
-
-bool
-ElevationManager::getElevationImpl(double x, double y,
-                                   double resolution,
-                                   const SpatialReference* srs,
-                                   double& out_elevation,
-                                   double& out_resolution)
-{
-    if ( _maxDataLevel == 0 || _tileSize == 0 )
-    {
-        // this means there are no heightfields.
-        out_elevation = 0.0;
-        return true;
-    }
-   
-    // this is the ideal LOD for the requested resolution:
-    unsigned int idealLevel = resolution > 0.0
-        ? _mapf.getProfile()->getLevelOfDetailForHorizResolution( resolution, _tileSize )
-        : _maxDataLevel;        
-
-    // based on the heightfields available, this is the best we can theorically do:
-    unsigned int bestAvailLevel = osg::minimum( idealLevel, _maxDataLevel );
-    if (_maxLevelOverride >= 0)
-    {
-        bestAvailLevel = osg::minimum(bestAvailLevel, (unsigned int)_maxLevelOverride);
-    }
-    
-    // transform the input coords to map coords:
-    double map_x = x, map_y = y;
-    if ( srs && !srs->isEquivalentTo( _mapf.getProfile()->getSRS() ) )
-    {
-        if ( !srs->transform2D( x, y, _mapf.getProfile()->getSRS(), map_x, map_y ) )
-        {
-            OE_WARN << LC << "Fail: coord transform failed" << std::endl;
-            return false;
-        }
-    }
-
-    osg::ref_ptr<osg::HeightField> hf;
-    osg::ref_ptr<osgTerrain::TerrainTile> tile;
-
-    // get the tilekey corresponding to the tile we need:
-    TileKey key = _mapf.getProfile()->createTileKey( map_x, map_y, bestAvailLevel );
-    if ( !key.valid() )
-    {
-        OE_WARN << LC << "Fail: coords fall outside map" << std::endl;
-        return false;
-    }
-
-    // now, see if we already have this tile loaded somewhere:
-    osgTerrain::TileID tileId = key.getTileId();
-
-    if ( !tile.valid() )
-    {
-        // next check the local tile cache:
-        TileTable::const_iterator i = _tileCache.find( tileId );
-        if ( i != _tileCache.end() )
-            tile = i->second.get();
-    }
-
-         
-    // if we found it, make sure it has a heightfield in it:
-    if ( tile.valid() )
-    {
-        osgTerrain::HeightFieldLayer* layer = dynamic_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer());
-        if ( layer )
-        {
-            hf = layer->getHeightField();
-        }
-        if ( !hf.valid() )
-        {
-            tile = NULL;
-        }
-    }
-
-    // if we didn't find it (or it didn't have heightfield data), build it.
-    if ( !tile.valid() )
-    {
-        //OE_NOTICE << "ElevationManager: cache miss" << std::endl;
-
-        // generate the heightfield corresponding to the tile key, automatically falling back
-        // on lower resolution if necessary:
-        _mapf.getHeightField( key, true, hf, 0L );
-
-        // bail out if we could not make a heightfield a all.
-        if ( !hf.valid() )
-        {
-            OE_WARN << "ElevationManager: unable to create heightfield" << std::endl;
-            return false;
-        }
-
-        GeoLocator* locator = GeoLocator::createForKey( key, _mapf.getMapInfo() );
-
-        tile = new osgTerrain::TerrainTile();
-
-        osgTerrain::HeightFieldLayer* layer = new osgTerrain::HeightFieldLayer( hf.get() );
-        layer->setLocator( locator );
-
-        tile->setElevationLayer( layer );
-        tile->setRequiresNormals( false );
-        tile->setTerrainTechnique( new osgTerrain::GeometryTechnique );
-
-        // store it in the local tile cache.
-        // TODO: limit the size of the cache with a parallel FIFO list.
-        _tileCache[tileId] = tile.get();
-        _tileCacheFIFO.push_back( tileId );
-
-        // prune the cache. this is a terrible pruning method.
-        if ( (int)_tileCache.size() > _maxCacheSize )
-        {
-            osgTerrain::TileID id = _tileCacheFIFO.front();
-            _tileCacheFIFO.pop_front();
-            if ( tileId != id )
-                _tileCache.erase( id );
-        }
-    }
-
-
-    // see what the actual resolution of the heightfield is.
-    out_resolution = (double)hf->getXInterval();
-
-
-    // finally it's time to get a height value:
-    if ( _technique == TECHNIQUE_PARAMETRIC )
-    {
-        const GeoExtent& extent = key.getExtent();
-        double xInterval = extent.width()  / (double)(hf->getNumColumns()-1);
-        double yInterval = extent.height() / (double)(hf->getNumRows()-1);
-        out_elevation = (double) HeightFieldUtils::getHeightAtLocation( hf.get(), map_x, map_y, extent.xMin(), extent.yMin(), xInterval, yInterval );
-        return true;
-    }
-    else // ( _technique == TECHNIQUE_GEOMETRIC )
-    {
-        osg::Vec3d start, end, zero;
-
-        if ( _mapf.getMapInfo().isGeocentric() )
-        {
-            const osg::EllipsoidModel* ellip = _mapf.getProfile()->getSRS()->getEllipsoid();
-
-            ellip->convertLatLongHeightToXYZ(
-                osg::DegreesToRadians( map_y ),
-                osg::DegreesToRadians( map_x ),
-                50000,
-                start.x(), start.y(), start.z() );
-
-            ellip->convertLatLongHeightToXYZ(
-                osg::DegreesToRadians( map_y ),
-                osg::DegreesToRadians( map_x ),
-                -50000,
-                end.x(), end.y(), end.z() );
-
-            ellip->convertLatLongHeightToXYZ(
-                osg::DegreesToRadians( map_y ),
-                osg::DegreesToRadians( map_x ),
-                0.0,
-                zero.x(), zero.y(), zero.z() );
-        }
-        else // PROJECTED
-        {
-            start.x() = map_x; start.y() = map_y; start.z() = 50000;
-            end.x() = map_x; end.y() = map_y; end.z() = -50000;
-            zero.x() = map_x; zero.y() = map_y; zero.z() = 0;
-        }
-
-        osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
-        osgUtil::IntersectionVisitor iv;
-        iv.setIntersector( i );
-
-        tile->accept( iv );
-
-        osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
-        if ( !results.empty() )
-        {
-            const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
-            osg::Vec3d isectPoint = result.getWorldIntersectPoint();
-            out_elevation = (isectPoint-end).length2() > (zero-end).length2()
-                ? (isectPoint-zero).length()
-                : -(isectPoint-zero).length();
-            return true;            
-        }
-
-        OE_WARN << "ElevationManager: no intersections" << std::endl;
-        return false;
-    }
-}
-
-
-bool 
-ElevationManager::getPlacementMatrix(double x, double y, double z,
-                                     double resolution,
-                                     const SpatialReference* srs,
-                                     osg::Matrixd& out_matrix,
-                                     double& out_elevation,
-                                     double& out_resolution)
-{
-    sync();
-
-    const SpatialReference* mapSRS = _mapf.getProfile()->getSRS();
-
-    // transform the input coords to map coords:
-    double map_x = x, map_y = y;
-    if ( srs && !srs->isEquivalentTo( mapSRS ) )
-    {
-        if ( !srs->transform2D( x, y, mapSRS, map_x, map_y ) )
-        {
-            OE_WARN << LC << "getPlacementMatrix: coord transform failed" << std::endl;
-            return false;
-        }
-    }
-
-    // get the elevation under those coordinates:
-    if ( !getElevationImpl( map_x, map_y, resolution, mapSRS, out_elevation, out_resolution) )
-    {
-        OE_WARN << LC << "getPlacementMatrix: getElevation failed" << std::endl;
-        return false;
-    }
-
-    if ( _mapf.getMapInfo().isGeocentric() )
-    {
-        mapSRS->getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight(
-            osg::DegreesToRadians( map_y ),
-            osg::DegreesToRadians( map_x ),
-            out_elevation + z,
-            out_matrix );
-    }
-    else
-    {
-        out_matrix = osg::Matrixd::translate( x, y, out_elevation + z );
-    }
-
-    return true;
-}
diff --git a/src/osgEarthUtil/ExampleResources b/src/osgEarthUtil/ExampleResources
index 0d091a0..c15914d 100644
--- a/src/osgEarthUtil/ExampleResources
+++ b/src/osgEarthUtil/ExampleResources
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ExampleResources.cpp b/src/osgEarthUtil/ExampleResources.cpp
index f551831..5e7f49d 100644
--- a/src/osgEarthUtil/ExampleResources.cpp
+++ b/src/osgEarthUtil/ExampleResources.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
 #include <osgEarthUtil/MGRSFormatter>
 #include <osgEarthUtil/MouseCoordsTool>
 #include <osgEarthUtil/AutoClipPlaneHandler>
+#include <osgEarthUtil/DataScanner>
 
 #include <osgEarthAnnotation/AnnotationData>
 #include <osgEarthAnnotation/AnnotationRegistry>
@@ -36,6 +37,7 @@
 #include <osgGA/StateSetManipulator>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgDB/FileNameUtils>
+#include <osgDB/WriteFile>
 
 #define KML_PUSHPIN_URL "http://demo.pelicanmapping.com/icons/pushpin_yellow.png"
 
@@ -84,6 +86,16 @@ namespace
 
         osg::observer_ptr<osg::Node> _node;
     };
+
+    // sets a user-specified uniform.
+    struct ApplyValueUniform : public ControlEventHandler
+    {
+        osg::ref_ptr<osg::Uniform> _u;
+        ApplyValueUniform(osg::Uniform* u) :_u(u) { }
+        void onValueChanged(Control* c, double value) {
+            _u->set( float(value) );
+        }
+    };
 }
 
 //------------------------------------------------------------------------
@@ -398,6 +410,10 @@ MapNodeHelper::load(osg::ArgumentParser& args,
                     osgViewer::View*     view,
                     Control*             userControl ) const
 {
+    // do this first before scanning for an earth file
+    std::string outEarth;
+    args.read( "--out-earth", outEarth );
+
     // read in the Earth file:
     osg::Node* node = 0L;
     for( int i=0; i<args.argc(); ++i )
@@ -412,16 +428,11 @@ MapNodeHelper::load(osg::ArgumentParser& args,
 
     if ( !node )
     {
-        OE_WARN << LC << "Unable to load an earth file from the command line." << std::endl;
-        return 0L;
-        //node = osgDB::readNodeFile( "gdal_tiff.earth" );
-        //if ( !node )
-        //{
-        //    return 0L;
-        //}
+        OE_WARN << LC << "No earth file from the command line; making one." << std::endl;
+        node = new MapNode();
     }
 
-    osg::ref_ptr<MapNode> mapNode = MapNode::findMapNode(node);
+    osg::ref_ptr<MapNode> mapNode = MapNode::get(node);
     if ( !mapNode.valid() )
     {
         OE_WARN << LC << "Loaded scene graph does not contain a MapNode - aborting" << std::endl;
@@ -429,10 +440,13 @@ MapNodeHelper::load(osg::ArgumentParser& args,
     }
 
     // warn about not having an earth manip
-    EarthManipulator* manip = dynamic_cast<EarthManipulator*>(view->getCameraManipulator());
-    if ( manip == 0L )
+    if ( view )
     {
-        OE_WARN << LC << "Helper used before installing an EarthManipulator" << std::endl;
+        EarthManipulator* manip = dynamic_cast<EarthManipulator*>(view->getCameraManipulator());
+        if ( manip == 0L )
+        {
+            OE_WARN << LC << "Helper used before installing an EarthManipulator" << std::endl;
+        }
     }
 
     // a root node to hold everything:
@@ -441,10 +455,23 @@ MapNodeHelper::load(osg::ArgumentParser& args,
     root->addChild( mapNode.get() );
 
     // parses common cmdline arguments.
-    parse( mapNode.get(), args, view, root, userControl );
+    if ( view )
+    {
+        parse( mapNode.get(), args, view, root, userControl );
+    }
+
+    // Dump out an earth file if so directed.
+    if ( !outEarth.empty() )
+    {
+        OE_NOTICE << LC << "Writing earth file: " << outEarth << std::endl;
+        osgDB::writeNodeFile( *mapNode, outEarth );
+    }
 
     // configures the viewer with some stock goodies
-    configureView( view );
+    if ( view )
+    {
+        configureView( view );
+    }
 
     return root;
 }
@@ -465,7 +492,6 @@ MapNodeHelper::parse(MapNode*             mapNode,
     osg::ref_ptr<osgDB::Options> dbOptions = Registry::instance()->cloneOrCreateOptions();
 
     // parse out custom example arguments first:
-
     bool useSky        = args.read("--sky");
     bool useOcean      = args.read("--ocean");
     bool useMGRS       = args.read("--mgrs");
@@ -481,6 +507,12 @@ MapNodeHelper::parse(MapNode*             mapNode,
     std::string kmlFile;
     args.read( "--kml", kmlFile );
 
+    std::string imageFolder;
+    args.read( "--images", imageFolder );
+
+    std::string imageExtensions;
+    args.read("--image-extensions", imageExtensions);
+
     // install a canvas for any UI controls we plan to create:
     ControlCanvas* canvas = ControlCanvas::get(view, false);
 
@@ -534,7 +566,8 @@ MapNodeHelper::parse(MapNode*             mapNode,
     {
         double hours = skyConf.value( "hours", 12.0 );
         SkyNode* sky = new SkyNode( mapNode->getMap() );
-        sky->setAmbientBrightness( ambientBrightness );
+        //sky->setAmbientBrightness( ambientBrightness );
+        sky->setAutoAmbience( true );
         sky->setDateTime( 2011, 3, 6, hours );
         sky->attach( view );
         root->addChild( sky );
@@ -631,17 +664,61 @@ MapNodeHelper::parse(MapNode*             mapNode,
     // Install an auto clip plane clamper
     if ( useAutoClip )
     {
-#if 0
-        HorizonClipNode* hcn = new HorizonClipNode( mapNode );
-        if ( mapNode->getNumParents() == 1 )
+        mapNode->addCullCallback( new AutoClipPlaneCullCallback(mapNode) );
+    }
+
+    // Scan for images if necessary.
+    if ( !imageFolder.empty() )
+    {
+        std::vector<std::string> extensions;
+        if ( !imageExtensions.empty() )
+            StringTokenizer( imageExtensions, extensions, ",;", "", false, true );
+        if ( extensions.empty() )
+            extensions.push_back( "tif" );
+
+        OE_INFO << LC << "Loading images from " << imageFolder << "..." << std::endl;
+        ImageLayerVector imageLayers;
+        DataScanner scanner;
+        scanner.findImageLayers( imageFolder, extensions, imageLayers );
+
+        if ( imageLayers.size() > 0 )
+        {
+            mapNode->getMap()->beginUpdate();
+            for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i )
+            {
+                mapNode->getMap()->addImageLayer( i->get() );
+            }
+            mapNode->getMap()->endUpdate();
+        }
+        OE_INFO << LC << "...found " << imageLayers.size() << " image layers." << std::endl;
+    }
+
+    // Generic named value uniform with min/max.
+    VBox* uniformBox = 0L;
+    while( args.find( "--uniform" ) >= 0 )
+    {
+        std::string name;
+        float minval, maxval;
+        if ( args.read( "--uniform", name, minval, maxval ) )
         {
-            osg::Group* parent = mapNode->getParent(0);
-            hcn->addChild( mapNode );
-            parent->replaceChild( mapNode, hcn );
+            if ( uniformBox == 0L )
+            {
+                uniformBox = new VBox();
+                uniformBox->setBackColor(0,0,0,0.5);
+                uniformBox->setAbsorbEvents( true );
+                canvas->addControl( uniformBox );
+            }
+            osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT, name);
+            uniform->set( minval );
+            root->getOrCreateStateSet()->addUniform( uniform, osg::StateAttribute::OVERRIDE );
+            HBox* box = new HBox();
+            box->addControl( new LabelControl(name) );
+            HSliderControl* hs = box->addControl( new HSliderControl(minval, maxval, minval, new ApplyValueUniform(uniform)));
+            hs->setHorizFill(true, 200);
+            box->addControl( new LabelControl(hs) );
+            uniformBox->addControl( box );
+            OE_INFO << LC << "Installed uniform controller for " << name << std::endl;
         }
-#else
-        mapNode->addCullCallback( new AutoClipPlaneCullCallback(mapNode) );
-#endif
     }
 
     root->addChild( canvas );
@@ -664,13 +741,16 @@ std::string
 MapNodeHelper::usage() const
 {
     return Stringify()
-        << "    --sky                : add a sky model\n"
-        << "    --ocean              : add an ocean model\n"
-        << "    --kml <file.kml>     : load a KML or KMZ file\n"
-        << "    --coords             : display map coords under mouse\n"
-        << "    --dms                : dispay deg/min/sec coords under mouse\n"
-        << "    --dd                 : display decimal degrees coords under mouse\n"
-        << "    --mgrs               : show MGRS coords under mouse\n"
-        << "    --ortho              : use an orthographic camera\n"
-        << "    --autoclip           : installs an auto-clip plane callback\n";
+        << "  --sky                         : add a sky model\n"
+        << "  --ocean                       : add an ocean model\n"
+        << "  --kml <file.kml>              : load a KML or KMZ file\n"
+        << "  --coords                      : display map coords under mouse\n"
+        << "  --dms                         : dispay deg/min/sec coords under mouse\n"
+        << "  --dd                          : display decimal degrees coords under mouse\n"
+        << "  --mgrs                        : show MGRS coords under mouse\n"
+        << "  --ortho                       : use an orthographic camera\n"
+        << "  --autoclip                    : installs an auto-clip plane callback\n"
+        << "  --images [path]               : finds and loads image layers from folder [path]\n"
+        << "  --image-extensions [ext,...]  : with --images, extensions to use\n"
+        << "  --out-earth [file]            : write the loaded map to an earth file\n";
 }
diff --git a/src/osgEarthUtil/Export b/src/osgEarthUtil/Export
index 508c135..d8f6c60 100644
--- a/src/osgEarthUtil/Export
+++ b/src/osgEarthUtil/Export
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/FeatureManipTool b/src/osgEarthUtil/FeatureManipTool
index e090956..7857b9a 100644
--- a/src/osgEarthUtil/FeatureManipTool
+++ b/src/osgEarthUtil/FeatureManipTool
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -47,7 +47,7 @@ namespace osgEarth { namespace Util
         /**
          * Constructs a new feature dragger tool.
          */
-        FeatureManipTool( MapNode* mapNode );
+        FeatureManipTool( MapNode* mapNode, bool verticalEnabled=false );
 
         /** dtor */
         virtual ~FeatureManipTool() { }
@@ -86,7 +86,7 @@ namespace osgEarth { namespace Util
 
     public: // internal
 
-        void syncToDraggers();
+        void syncToDraggers(bool wasVertical=false);
 
     protected:
 
@@ -106,9 +106,12 @@ namespace osgEarth { namespace Util
         FeatureDrawSet                     _drawSet;
         osg::ref_ptr<osg::MatrixTransform> _manipModel;
         osg::ref_ptr<osg::Node>            _ghostModel;
+        bool                               _verticalEnabled;
         double                             _verticalOffset;
+        double                             _verticalDraggerOffset;
         osg::ref_ptr<CircleNode>           _circle;
         osg::ref_ptr<CircleNodeEditor>     _circleEditor;
+        osg::ref_ptr<SphereDragger>        _verticalDragger;
         osg::ref_ptr<osg::Group>           _workGroup;
     };
 
diff --git a/src/osgEarthUtil/FeatureManipTool.cpp b/src/osgEarthUtil/FeatureManipTool.cpp
index 8ffdb0d..e6a3698 100644
--- a/src/osgEarthUtil/FeatureManipTool.cpp
+++ b/src/osgEarthUtil/FeatureManipTool.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -97,14 +97,15 @@ namespace
     // Dragger callback to simply hooks back into the DraggerTool.
     struct DraggerCallback : public Dragger::PositionChangedCallback
     {
-        DraggerCallback( FeatureManipTool* tool ) : _tool(tool) { }
+        DraggerCallback( FeatureManipTool* tool, bool isVertical=false ) : _tool(tool), _isVertical(isVertical) { }
 
         void onPositionChanged(const Dragger* sender, const osgEarth::GeoPoint& pos)
         {
-            _tool->syncToDraggers();
+            _tool->syncToDraggers(_isVertical);
         }
 
         FeatureManipTool* _tool;
+        bool _isVertical;
     };
 
     // updates the verts in a subgraph based on a pair of re-positioning transforms.
@@ -139,8 +140,10 @@ namespace
 
 //-----------------------------------------------------------------------
 
-FeatureManipTool::FeatureManipTool(MapNode* mapNode) :
-FeatureQueryTool( mapNode )
+FeatureManipTool::FeatureManipTool(MapNode* mapNode, bool verticalEnabled) :
+FeatureQueryTool( mapNode ),
+_verticalEnabled(verticalEnabled),
+_verticalDraggerOffset(0.0)
 {
     // install this object as a query callback so we will receive messages.
     this->addCallback( this );
@@ -166,14 +169,6 @@ FeatureManipTool::onHit( FeatureSourceIndexNode* index, FeatureID fid, const Eve
         osg::Vec3d anchorWorld;
         anchorWorld = args._worldPoint;
 
-        // calculate the vertical offset of the mouse's hit point from the ground
-        GeoPoint hitMap;
-        hitMap.fromWorld( _mapNode->getMapSRS(), args._worldPoint );
-        double hae;
-        _verticalOffset = 0.0;
-        if (_mapNode->getTerrain()->getHeight( hitMap.getSRS(), hitMap.x(), hitMap.y(), 0L, &hae ))
-            _verticalOffset = hitMap.z() - hae;
-
         // extract the "hit" feature from its draw set into a new draggable node.
         osg::ref_ptr<osg::Node> node;
 
@@ -187,12 +182,17 @@ FeatureManipTool::onHit( FeatureSourceIndexNode* index, FeatureID fid, const Eve
             anchorWorld = manipModel->getBound().center();
             
             // calculate the vertical offset of the anchor point from the ground
-            GeoPoint anchorMap;
-            anchorMap.fromWorld( _mapNode->getMapSRS(), anchorWorld );
+            GeoPoint anchorMapCenter;
+            anchorMapCenter.fromWorld( _mapNode->getMapSRS(), anchorWorld );
+            _verticalOffset = anchorMapCenter.z();
+
+            GeoPoint anchorMap(anchorMapCenter);
             anchorMap.z() = 0;
             anchorMap.altitudeMode() = ALTMODE_RELATIVE;
             anchorMap.transformZ( ALTMODE_ABSOLUTE, _mapNode->getTerrain() );
 
+            _verticalOffset = anchorMapCenter.z() - anchorMap.z();
+
             anchorMap.toWorld( anchorWorld );
 
             // set up the dragged model's appearance:
@@ -207,7 +207,7 @@ FeatureManipTool::onHit( FeatureSourceIndexNode* index, FeatureID fid, const Eve
             // system around the mouse. This will allow us to move the feature without messing around
             // with its relatively-positioned verts.
             osg::Matrixd world2local_anchor;
-            anchorMap.createWorldToLocal( world2local_anchor );
+            anchorMapCenter.createWorldToLocal( world2local_anchor );
 
             osg::MatrixTransform* world2local_xform = new osg::MatrixTransform(world2local_anchor);
             world2local_xform->addChild( manipModel );
@@ -229,7 +229,7 @@ FeatureManipTool::onHit( FeatureSourceIndexNode* index, FeatureID fid, const Eve
             circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Yellow, 0.25);
             circleStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::White;
             const osg::BoundingSphere& bs = manipModel->getBound();
-            _circle = new CircleNode( getMapNode(), anchorMap, Distance(bs.radius()*1.5), circleStyle, false );
+            _circle = new CircleNode( getMapNode(), anchorMap, Distance(bs.radius()*1.5), circleStyle );
             _circle->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS,0,1,false) );
 
             _circleEditor = new CircleNodeEditor( _circle.get() );
@@ -237,12 +237,32 @@ FeatureManipTool::onHit( FeatureSourceIndexNode* index, FeatureID fid, const Eve
             _circleEditor->getRadiusDragger()->addPositionChangedCallback( new DraggerCallback(this) );
             _circleEditor->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS,0,1,false) );
 
+            // add an additional dragger for vertical dragging
+            if (_verticalEnabled)
+            {
+                _verticalDragger  = new SphereDragger( getMapNode() );
+                _verticalDragger->setDefaultDragMode(Dragger::DRAGMODE_VERTICAL);
+                _verticalDragger->setColor(osg::Vec4f(0.0f, 1.0f, 1.0f, 1.0f));
+                _verticalDragger->setPickColor(osg::Vec4f(1.0f, 0.0f, 1.0f, 1.0f));
+
+                _verticalDraggerOffset = bs.radius() * 1.1;
+                GeoPoint verticalDraggerPos(anchorMapCenter);
+                verticalDraggerPos.z() += _verticalDraggerOffset;
+                _verticalDragger->setPosition(verticalDraggerPos, false);
+                _verticalDragger->setVerticalMinimum(_verticalDraggerOffset);
+
+                _verticalDragger->addPositionChangedCallback( new DraggerCallback(this, true) );
+            }
+
             // micro-manage the render order to get things just right:
             _workGroup->getOrCreateStateSet()->setRenderBinDetails( 15, "TraversalOrderBin" );
             _workGroup->addChild( _circle.get() );
             _workGroup->addChild( _manipModel.get() );
             _workGroup->addChild( _ghostModel.get() );
             _workGroup->addChild( _circleEditor.get() );
+
+            if (_verticalDragger.valid())
+                _workGroup->addChild( _verticalDragger.get() );
         }
     }
 }
@@ -294,12 +314,27 @@ FeatureManipTool::setRotation( const Angle& rot )
 
 
 void
-FeatureManipTool::syncToDraggers()
+FeatureManipTool::syncToDraggers(bool wasVertical)
 {
     // position the feature based on the circle annotation's draggers:
     GeoPoint pos = _circleEditor->getPositionDragger()->getPosition();
     GeoPoint rad = _circleEditor->getRadiusDragger()->getPosition();
 
+    // if the vertical dragger was moved, update the vertical offset
+    if (wasVertical)
+      _verticalOffset = _verticalDragger->getPosition().z() - _verticalDraggerOffset - pos.z();
+
+    GeoPoint vPos(pos);
+    vPos.z() += _verticalOffset;
+
+    // update the vertical draggers (horizontal) position 
+    if (_verticalEnabled)
+    {
+      GeoPoint vdPos(vPos);
+      vdPos.z() += _verticalDraggerOffset;
+      _verticalDragger->setPosition(vdPos, false);
+    }
+    
     pos.makeGeographic();
     rad.makeGeographic();
 
@@ -308,7 +343,7 @@ FeatureManipTool::syncToDraggers()
         osg::DegreesToRadians(rad.y()), osg::DegreesToRadians(rad.x()) );
 
     osg::Matrixd local2world;
-    pos.createLocalToWorld( local2world );
+    vPos.createLocalToWorld( local2world );
 
     // rotate the feature:
     osg::Quat rot( osg::PI_2-bearing, osg::Vec3d(0,0,1) );
diff --git a/src/osgEarthUtil/FeatureQueryTool b/src/osgEarthUtil/FeatureQueryTool
index 7a1e209..cbb75ea 100644
--- a/src/osgEarthUtil/FeatureQueryTool
+++ b/src/osgEarthUtil/FeatureQueryTool
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/FeatureQueryTool.cpp b/src/osgEarthUtil/FeatureQueryTool.cpp
index afb7eaf..e357e79 100644
--- a/src/osgEarthUtil/FeatureQueryTool.cpp
+++ b/src/osgEarthUtil/FeatureQueryTool.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/Formatter b/src/osgEarthUtil/Formatter
index 07d2982..dc26879 100644
--- a/src/osgEarthUtil/Formatter
+++ b/src/osgEarthUtil/Formatter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/RGBColorFilter b/src/osgEarthUtil/GLSLColorFilter
similarity index 63%
copy from src/osgEarthUtil/RGBColorFilter
copy to src/osgEarthUtil/GLSLColorFilter
index 3932445..4093d24 100644
--- a/src/osgEarthUtil/RGBColorFilter
+++ b/src/osgEarthUtil/GLSLColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -15,11 +15,9 @@
 *
 * 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/>
-*
-* Original author: Thomas Lerman
 */
-#ifndef OSGEARTHUTIL_RGB_COLOR_FILTER
-#define OSGEARTHUTIL_RGB_COLOR_FILTER
+#ifndef OSGEARTHUTIL_GLSL_COLOR_FILTER
+#define OSGEARTHUTIL_GLSL_COLOR_FILTER
 
 #include <osgEarthUtil/Common>
 #include <osgEarth/ColorFilter>
@@ -28,20 +26,17 @@
 namespace osgEarth { namespace Util
 {
     /**
-    * Color filter that adjust the red/green/blue of a texel.
-    */
-    class OSGEARTHUTIL_EXPORT RGBColorFilter : public osgEarth::ColorFilter
+     * Color filter that lets you inject custom GLSL code.
+     */
+    class OSGEARTHUTIL_EXPORT GLSLColorFilter : public osgEarth::ColorFilter
     {
     public:
-        RGBColorFilter();
-        RGBColorFilter(const Config& conf);
-        virtual ~RGBColorFilter() { }
+        GLSLColorFilter();
+        GLSLColorFilter(const Config& conf);
+        virtual ~GLSLColorFilter() { }
 
-        /**
-        * The red/green/blue offset, each component is [-1..1] (no change is at 0)
-        */
-        void setRGBOffset(const osg::Vec3f& rgb);
-        osg::Vec3f getRGBOffset(void) const;
+        void setCode(const std::string& code) { _code = code; }
+        const std::string& getCode() const { return _code; }
 
     public: // ColorFilter
         virtual std::string getEntryPointFunctionName(void) const;
@@ -50,11 +45,10 @@ namespace osgEarth { namespace Util
 
     protected:
         unsigned m_instanceId;
-        osg::ref_ptr<osg::Uniform> m_rgb;
-
         void init();
+        std::string _code;
     };
 
 } } // namespace osgEarth::Util
 
-#endif // OSGEARTHUTIL_RGB_COLOR_FILTER
+#endif // OSGEARTHUTIL_GLSL_COLOR_FILTER
diff --git a/src/osgEarthUtil/RGBColorFilter.cpp b/src/osgEarthUtil/GLSLColorFilter.cpp
similarity index 57%
copy from src/osgEarthUtil/RGBColorFilter.cpp
copy to src/osgEarthUtil/GLSLColorFilter.cpp
index cc53e3f..bbafdfd 100644
--- a/src/osgEarthUtil/RGBColorFilter.cpp
+++ b/src/osgEarthUtil/GLSLColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
 *
 * Original author: Thomas Lerman
 */
-#include <osgEarthUtil/RGBColorFilter>
+#include <osgEarthUtil/GLSLColorFilter>
 #include <osgEarth/VirtualProgram>
 #include <osgEarth/StringUtils>
 #include <osgEarth/ThreadingUtils>
@@ -33,58 +33,40 @@ namespace
     static OpenThreads::Atomic s_uniformNameGen;
 
     static const char* s_localShaderSource =
-        "#version 110\n"
-        "uniform vec3 __UNIFORM_NAME__;\n"
-
+        "#version " GLSL_VERSION_STR "\n"
+        GLSL_DEFAULT_PRECISION_FLOAT "\n"
         "void __ENTRY_POINT__(in int slot, inout vec4 color)\n"
         "{\n"
-        "    color.rgb = clamp(color.rgb + __UNIFORM_NAME__.rgb, 0.0, 1.0); \n"
+        "__CODE__ \n"
         "} \n";
 }
 
 //---------------------------------------------------------------------------
 
-#define FUNCTION_PREFIX "osgearthutil_rgbColorFilter_"
-#define UNIFORM_PREFIX  "osgearthutil_u_rgb_"
+#define FUNCTION_PREFIX "oe_glsl_color_filter"
 
 //---------------------------------------------------------------------------
 
-RGBColorFilter::RGBColorFilter(void)
+GLSLColorFilter::GLSLColorFilter()
 {
     init();
 }
 
-void RGBColorFilter::init()
+void 
+GLSLColorFilter::init()
 {
-    // Generate a unique name for this filter's uniform. This is necessary
-    // so that each layer can have a unique uniform and entry point.
     m_instanceId = (++s_uniformNameGen) - 1;
-    m_rgb = new osg::Uniform(osg::Uniform::FLOAT_VEC3, (osgEarth::Stringify() << UNIFORM_PREFIX << m_instanceId));
-    m_rgb->set(osg::Vec3f(0.0f, 0.0f, 0.0f));
 }
 
-void RGBColorFilter::setRGBOffset(const osg::Vec3f& value)
-{
-    m_rgb->set(value);
-}
-
-osg::Vec3f RGBColorFilter::getRGBOffset(void) const
-{
-    osg::Vec3f value;
-    m_rgb->get(value);
-    return (value);
-}
-
-std::string RGBColorFilter::getEntryPointFunctionName(void) const
+std::string
+GLSLColorFilter::getEntryPointFunctionName(void) const
 {
     return (osgEarth::Stringify() << FUNCTION_PREFIX << m_instanceId);
 }
 
-void RGBColorFilter::install(osg::StateSet* stateSet) const
+void 
+GLSLColorFilter::install(osg::StateSet* stateSet) const
 {
-    // safe: will not add twice.
-    stateSet->addUniform(m_rgb.get());
-
     osgEarth::VirtualProgram* vp = dynamic_cast<osgEarth::VirtualProgram*>(stateSet->getAttribute(VirtualProgram::SA_TYPE));
     if (vp)
     {
@@ -92,11 +74,10 @@ void RGBColorFilter::install(osg::StateSet* stateSet) const
         // use a template with search and replace for this one.
         std::string entryPoint = osgEarth::Stringify() << FUNCTION_PREFIX << m_instanceId;
         std::string code = s_localShaderSource;
-        osgEarth::replaceIn(code, "__UNIFORM_NAME__", m_rgb->getName());
         osgEarth::replaceIn(code, "__ENTRY_POINT__", entryPoint);
+        osgEarth::replaceIn(code, "__CODE__", _code);
 
         osg::Shader* main = new osg::Shader(osg::Shader::FRAGMENT, code);
-        //main->setName(entryPoint);
         vp->setShader(entryPoint, main);
     }
 }
@@ -104,27 +85,18 @@ void RGBColorFilter::install(osg::StateSet* stateSet) const
 
 //---------------------------------------------------------------------------
 
-OSGEARTH_REGISTER_COLORFILTER( rgb, osgEarth::Util::RGBColorFilter );
+OSGEARTH_REGISTER_COLORFILTER( glsl, osgEarth::Util::GLSLColorFilter );
 
 
-RGBColorFilter::RGBColorFilter(const Config& conf)
+GLSLColorFilter::GLSLColorFilter(const Config& conf)
 {
     init();
-
-    osg::Vec3f val;
-    val[0] = conf.value("r", 0.0);
-    val[1] = conf.value("g", 0.0);
-    val[2] = conf.value("b", 0.0);
-    setRGBOffset( val );
+    setCode( conf.value() );
 }
 
 Config
-RGBColorFilter::getConfig() const
+GLSLColorFilter::getConfig() const
 {
-    osg::Vec3f val = getRGBOffset();
-    Config conf("rgb");
-    conf.add( "r", val[0] );
-    conf.add( "g", val[1] );
-    conf.add( "b", val[2] );
+    Config conf("glsl", getCode());
     return conf;
 }
diff --git a/src/osgEarthUtil/GammaColorFilter b/src/osgEarthUtil/GammaColorFilter
index 4418e16..20d68a9 100644
--- a/src/osgEarthUtil/GammaColorFilter
+++ b/src/osgEarthUtil/GammaColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/GammaColorFilter.cpp b/src/osgEarthUtil/GammaColorFilter.cpp
index 1f53a58..adb96f5 100644
--- a/src/osgEarthUtil/GammaColorFilter.cpp
+++ b/src/osgEarthUtil/GammaColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/GeodeticGraticule b/src/osgEarthUtil/GeodeticGraticule
index 12bbbb7..33e9eec 100644
--- a/src/osgEarthUtil/GeodeticGraticule
+++ b/src/osgEarthUtil/GeodeticGraticule
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/GeodeticGraticule.cpp b/src/osgEarthUtil/GeodeticGraticule.cpp
index cce212d..6b6322a 100644
--- a/src/osgEarthUtil/GeodeticGraticule.cpp
+++ b/src/osgEarthUtil/GeodeticGraticule.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -352,8 +352,11 @@ GeodeticGraticule::buildTile( const TileKey& key, Map* map ) const
     // get the geocentric tile center:
     osg::Vec3d tileCenter;
     tileExtent.getCentroid( tileCenter.x(), tileCenter.y() );
+
+    const SpatialReference* ecefSRS = tileExtent.getSRS()->getECEF();
     osg::Vec3d centerECEF;
-    tileExtent.getSRS()->transformToECEF( tileCenter, centerECEF );
+    tileExtent.getSRS()->transform( tileCenter, ecefSRS, centerECEF );
+    //tileExtent.getSRS()->transformToECEF( tileCenter, centerECEF );
 
     osg::NodeCallback* ccc = 0L;
     // set up cluster culling.
diff --git a/src/osgEarthUtil/HSLColorFilter b/src/osgEarthUtil/HSLColorFilter
index f542584..39671c7 100644
--- a/src/osgEarthUtil/HSLColorFilter
+++ b/src/osgEarthUtil/HSLColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/HSLColorFilter.cpp b/src/osgEarthUtil/HSLColorFilter.cpp
index 7efd4a2..b1c6892 100644
--- a/src/osgEarthUtil/HSLColorFilter.cpp
+++ b/src/osgEarthUtil/HSLColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/LatLongFormatter b/src/osgEarthUtil/LatLongFormatter
index 64e320d..3e0f4eb 100644
--- a/src/osgEarthUtil/LatLongFormatter
+++ b/src/osgEarthUtil/LatLongFormatter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/LatLongFormatter.cpp b/src/osgEarthUtil/LatLongFormatter.cpp
index 309dd7d..b250ca3 100644
--- a/src/osgEarthUtil/LatLongFormatter.cpp
+++ b/src/osgEarthUtil/LatLongFormatter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/LineOfSight b/src/osgEarthUtil/LineOfSight
index ab039b3..7fc9659 100644
--- a/src/osgEarthUtil/LineOfSight
+++ b/src/osgEarthUtil/LineOfSight
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/LinearLineOfSight b/src/osgEarthUtil/LinearLineOfSight
index 8d7f454..a8a1f31 100644
--- a/src/osgEarthUtil/LinearLineOfSight
+++ b/src/osgEarthUtil/LinearLineOfSight
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/LinearLineOfSight.cpp b/src/osgEarthUtil/LinearLineOfSight.cpp
index 671ffbd..db0dcf9 100644
--- a/src/osgEarthUtil/LinearLineOfSight.cpp
+++ b/src/osgEarthUtil/LinearLineOfSight.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/MGRSFormatter b/src/osgEarthUtil/MGRSFormatter
index 91c59c9..9003f52 100644
--- a/src/osgEarthUtil/MGRSFormatter
+++ b/src/osgEarthUtil/MGRSFormatter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/MGRSFormatter.cpp b/src/osgEarthUtil/MGRSFormatter.cpp
index cd770d2..da25034 100644
--- a/src/osgEarthUtil/MGRSFormatter.cpp
+++ b/src/osgEarthUtil/MGRSFormatter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/MGRSGraticule b/src/osgEarthUtil/MGRSGraticule
index 3d69b8d..ee2113d 100644
--- a/src/osgEarthUtil/MGRSGraticule
+++ b/src/osgEarthUtil/MGRSGraticule
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/MGRSGraticule.cpp b/src/osgEarthUtil/MGRSGraticule.cpp
index f3643ce..cff362e 100644
--- a/src/osgEarthUtil/MGRSGraticule.cpp
+++ b/src/osgEarthUtil/MGRSGraticule.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -137,11 +137,14 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
     textGeode->getOrCreateStateSet()->setRenderBinDetails( 9999, "DepthSortedBin" );    
     textGeode->getOrCreateStateSet()->setAttributeAndModes( _depthAttribute, 1 );
 
+    const SpatialReference* ecefSRS = extent.getSRS()->getECEF();
     osg::Vec3d centerMap, centerECEF;
     extent.getCentroid(centerMap.x(), centerMap.y());
-    extent.getSRS()->transformToECEF(centerMap, centerECEF);
+    extent.getSRS()->transform(centerMap, ecefSRS, centerECEF);
+    //extent.getSRS()->transformToECEF(centerMap, centerECEF);
 
-    osg::Matrix local2world = ECEF::createLocalToWorld(centerECEF);
+    osg::Matrix local2world;
+    ecefSRS->createLocalToWorld( centerECEF, local2world ); //= ECEF::createLocalToWorld(centerECEF);
     osg::Matrix world2local;
     world2local.invert(local2world);
 
@@ -246,7 +249,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                 osg::Vec3d sqidTextMap = (nw + se) * 0.5;
                 sqidTextMap.z() += 1000.0;
                 osg::Vec3d sqidTextECEF;
-                extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
+                extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF);
+                //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
                 osg::Vec3d sqidLocal;
                 sqidLocal = sqidTextECEF * world2local;
 
@@ -256,7 +260,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                     textSym->size() = utmWidth/3.0;        
                     osgText::Text* d = ts.create( mgrsCoord.sqid );
 
-                    osg::Matrixd textLocal2World = ECEF::createLocalToWorld( sqidTextECEF );
+                    osg::Matrixd textLocal2World;
+                    ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World );
 
                     d->setPosition( sqidLocal );
                     textGeode->addDrawable( d );
@@ -315,7 +320,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                     {
                         sqidTextMap.z() += 1000.0;
                         osg::Vec3d sqidTextECEF;
-                        extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
+                        extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF);
+                        //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
                         osg::Vec3d sqidLocal = sqidTextECEF * world2local;
 
                         MGRSCoord mgrsCoord;
@@ -323,7 +329,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                         {
                             textSym->size() = 33000.0;
                             osgText::Text* d = ts.create( mgrsCoord.sqid );
-                            osg::Matrixd textLocal2World = ECEF::createLocalToWorld( sqidTextECEF );
+                            osg::Matrixd textLocal2World;
+                            ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World );
                             d->setPosition( sqidLocal );
                             textGeode->addDrawable( d );
                         }
@@ -370,7 +377,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                     {
                         sqidTextMap.z() += 1000.0;
                         osg::Vec3d sqidTextECEF;
-                        extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
+                        extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF);
+                        //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
                         osg::Vec3d sqidLocal = sqidTextECEF * world2local;
 
                         MGRSCoord mgrsCoord;
@@ -378,7 +386,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                         {
                             textSym->size() = 33000.0;
                             osgText::Text* d = ts.create( mgrsCoord.sqid );
-                            osg::Matrixd textLocal2World = ECEF::createLocalToWorld( sqidTextECEF );
+                            osg::Matrixd textLocal2World;
+                            ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World );
                             d->setPosition( sqidLocal );
                             textGeode->addDrawable( d );
                         }
@@ -438,7 +447,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                     {
                         sqidTextMap.z() += 1000.0;
                         osg::Vec3d sqidTextECEF;
-                        extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
+                        extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF);
+                        //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
                         osg::Vec3d sqidLocal = sqidTextECEF * world2local;
 
                         MGRSCoord mgrsCoord;
@@ -446,7 +456,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                         {
                             textSym->size() = 33000.0;
                             osgText::Text* d = ts.create( mgrsCoord.sqid );
-                            osg::Matrixd textLocal2World = ECEF::createLocalToWorld( sqidTextECEF );
+                            osg::Matrixd textLocal2World;
+                            ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World );
                             d->setPosition( sqidLocal );
                             textGeode->addDrawable( d );
                         }
@@ -493,7 +504,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                     {
                         sqidTextMap.z() += 1000.0;
                         osg::Vec3d sqidTextECEF;
-                        extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
+                        extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF);
+                        //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF);
                         osg::Vec3d sqidLocal = sqidTextECEF * world2local;
 
                         MGRSCoord mgrsCoord;
@@ -501,7 +513,8 @@ MGRSGraticule::buildSQIDTiles( const std::string& gzd )
                         {
                             textSym->size() = 33000.0;
                             osgText::Text* d = ts.create( mgrsCoord.sqid );
-                            osg::Matrixd textLocal2World = ECEF::createLocalToWorld( sqidTextECEF );
+                            osg::Matrixd textLocal2World;
+                            ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World );
                             d->setPosition( sqidLocal );
                             textGeode->addDrawable( d );
                         }
diff --git a/src/osgEarthUtil/MeasureTool b/src/osgEarthUtil/MeasureTool
index 5dd2be6..0f72252 100644
--- a/src/osgEarthUtil/MeasureTool
+++ b/src/osgEarthUtil/MeasureTool
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/MeasureTool.cpp b/src/osgEarthUtil/MeasureTool.cpp
index e8b15cf..cd17959 100644
--- a/src/osgEarthUtil/MeasureTool.cpp
+++ b/src/osgEarthUtil/MeasureTool.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -87,6 +87,7 @@ MeasureToolHandler::rebuild()
 
     AltitudeSymbol* alt = new AltitudeSymbol();
     alt->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
+    alt->technique() = AltitudeSymbol::TECHNIQUE_GPU;
 
     // Define the path feature:
     _feature = new Feature(new LineString(), getMapNode()->getMapSRS());
diff --git a/src/osgEarthUtil/MouseCoordsTool b/src/osgEarthUtil/MouseCoordsTool
index 2f77abc..1a826d7 100644
--- a/src/osgEarthUtil/MouseCoordsTool
+++ b/src/osgEarthUtil/MouseCoordsTool
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/MouseCoordsTool.cpp b/src/osgEarthUtil/MouseCoordsTool.cpp
index 1b8876b..956729d 100644
--- a/src/osgEarthUtil/MouseCoordsTool.cpp
+++ b/src/osgEarthUtil/MouseCoordsTool.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ObjectPlacer b/src/osgEarthUtil/ObjectPlacer
deleted file mode 100644
index fbf16f8..0000000
--- a/src/osgEarthUtil/ObjectPlacer
+++ /dev/null
@@ -1,122 +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 OSGEARTHUTIL_OBJECT_PLACER
-#define OSGEARTHUTIL_OBJECT_PLACER
-
-#include <osgEarthUtil/Common>
-#include <osgEarth/MapNode>
-#include <osg/Node>
-#include <osg/Matrix>
-#include <osgUtil/IntersectionVisitor>
-
-namespace osgEarth { namespace Util
-{
-    /**
-     * @deprecated Please use Annotation::GeometryNode instead.
-     *
-     * Convenience utilities for placing an object on an osgEarth terrain map
-     * using latitude/longitude coordinates.
-     */
-    class OSGEARTHUTIL_EXPORT ObjectPlacer
-    {
-    public:
-        /**
-         * @deprecated Please use Annotation::GeometryNode instead.
-         *
-         * Constructs a new placer.
-         *
-         * @param terrain
-         *      The scene graph containing the osgEarth::Map node.
-         * @param traversalMask
-         *      Mask to use when intersecting the terrain.
-         * @param clamp
-         *      Whether the class should attempt to calculate the placement 
-         *      position so that it sits exactly on the terrain skin.
-         *      Warning: this does not yet work properly for maps that don't
-         *      report a maximum resolution (like most of the commercial providers).
-         * @param maxLevel
-         *      Maximum level of detail to which to search for high resolution terrain.
-         */
-        ObjectPlacer(
-            osg::Node* terrain,
-            int  traversalMask =~0,
-            bool clamp        =false,
-            int  maxLevel     =20);
-
-        virtual ~ObjectPlacer() { }
-
-        /**
-         * @deprecated Please use Annotation::GeometryNode instead.
-         *
-         * Creates a double-precision matrix that will transform geometry to the
-         * specified map location. In a geocentric map, the matrix will also rotate
-         * into the tangent plane.
-         *
-         * @param lat_degrees, lon_degrees
-         *      Location on the map for which to generate a matrix
-         * @param height
-         *      Height above the terrain (in local units)
-         * @param out_result
-         *      Receives the resulting matrix; only valid if the method returns TRUE.
-         * @return
-         *      True if the method succesfully created the output matrix; false if not
-         *      (e.g., the input location was outside the extents of the terrain map).
-         */
-        bool createPlacerMatrix(
-            double lat_degrees,
-            double lon_degrees,
-            double height,
-            osg::Matrixd& out_result ) const;
-
-        /**
-         * @deprecated Please use Annotation::GeometryNode instead.
-         *
-         * Creates a new node graph that positions the input node at a specified map
-         * location. The resulting node will contain the input node as a child.
-         *
-         * @param node
-         *      Node to position at the specified location
-         * @param lat_degrees, lon_degrees
-         *      Position on the map at which to place the node
-         * @param height
-         *      Height above the terrain (in local units)
-         * @return
-         *      A node graph representing the newly placed node. The input node will be
-         *      a child in the new graph. NULL if the placement failed for some reason.
-         */
-        osg::Node* placeNode(
-            osg::Node* node,
-            double lat_degrees,
-            double lon_degrees,
-            double height ) const;
-
-    private:
-        osg::ref_ptr<osgEarth::MapNode> _mapNode;
-        osg::ref_ptr<osg::CoordinateSystemNode> _csn;
-        osg::ref_ptr<osgUtil::IntersectionVisitor::ReadCallback> _readCallback;
-        int _traversalMask;
-        bool _clamp;
-
-        bool clampGeocentric(osg::CoordinateSystemNode* csn, double lat_rad, double lon_rad, osg::Vec3d& out) const;
-        bool clampProjected(osg::CoordinateSystemNode* csn, double x, double y, osg::Vec3d& out) const;
-    };
-
-} } // namespace osgEarth::Util
-
-#endif // OSGEARTHUTIL_OBJECT_PLACER
diff --git a/src/osgEarthUtil/ObjectPlacer.cpp b/src/osgEarthUtil/ObjectPlacer.cpp
deleted file mode 100644
index 20bd4ce..0000000
--- a/src/osgEarthUtil/ObjectPlacer.cpp
+++ /dev/null
@@ -1,180 +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 <osgEarthUtil/ObjectPlacer>
-#include <osgEarth/NodeUtils>
-#include <osgEarth/MapNode>
-#include <osgEarth/SpatialReference>
-#include <osgSim/LineOfSight>
-#include <osgUtil/IntersectionVisitor>
-#include <osgUtil/LineSegmentIntersector>
-#include <osg/MatrixTransform>
-#include <osg/CoordinateSystemNode>
-
-using namespace osgEarth::Util;
-using namespace osgEarth;
-
-struct CachingReadCallback : public osgSim::DatabaseCacheReadCallback
-{
-    CachingReadCallback(int maxReads) : _reads(0), _maxReads(maxReads) { }
-    void reset() { _reads = 0; }
-    virtual osg::Node* readNodeFile(const std::string& filename) {
-        if ( _reads < _maxReads ) {
-            _reads++;
-            return osgSim::DatabaseCacheReadCallback::readNodeFile(filename);
-        }
-        else {
-            return NULL;
-        }
-    }
-    int _reads, _maxReads;
-    osg::ref_ptr<osg::Node> _lastNodeRead;
-};
-
-ObjectPlacer::ObjectPlacer( osg::Node* terrain, int traversalMask, bool clamp, int maxLevel ) :
-_traversalMask( traversalMask ),
-_clamp( clamp )
-{
-    _mapNode = findTopMostNodeOfType<osgEarth::MapNode>( terrain );
-    _csn = findTopMostNodeOfType<osg::CoordinateSystemNode>( terrain );
-    _readCallback = new CachingReadCallback( maxLevel );
-}
-
-bool
-ObjectPlacer::clampGeocentric( osg::CoordinateSystemNode* csn, double lat_rad, double lon_rad, osg::Vec3d& out ) const
-{
-    osg::Vec3d start, end;
-    
-    csn->getEllipsoidModel()->convertLatLongHeightToXYZ( lat_rad, lon_rad, 50000, start.x(), start.y(), start.z() );
-    csn->getEllipsoidModel()->convertLatLongHeightToXYZ( lat_rad, lon_rad, -50000, end.x(), end.y(), end.z() );
-    osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
-    
-    osgUtil::IntersectionVisitor iv;
-    iv.setIntersector( i );
-    static_cast<CachingReadCallback*>(_readCallback.get())->reset();
-    iv.setReadCallback( _readCallback.get() );
-    iv.setTraversalMask( _traversalMask );
-
-    _mapNode->accept( iv );
-
-    osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
-    if ( !results.empty() )
-    {
-        const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
-        out = result.matrix.valid() ?
-            result.localIntersectionPoint * (*result.matrix) :
-            result.localIntersectionPoint;
-        return true;            
-    }
-    return false;
-}
-
-bool
-ObjectPlacer::clampProjected( osg::CoordinateSystemNode* csn, double x, double y, osg::Vec3d& out ) const
-{
-    osg::Vec3d start( x, y, 50000 );
-    osg::Vec3d end(x, y, -50000);
-    osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
-    
-    osgUtil::IntersectionVisitor iv;
-    iv.setIntersector( i );
-    static_cast<CachingReadCallback*>(_readCallback.get())->reset();
-    iv.setReadCallback( _readCallback.get() );
-    iv.setTraversalMask( _traversalMask );
-
-    _mapNode->accept( iv );
-
-    osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
-    if ( !results.empty() )
-    {
-        const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
-        out = result.matrix.valid() ?
-            result.localIntersectionPoint * (*result.matrix) :
-            result.localIntersectionPoint;
-        return true;            
-    }
-    return false;
-}
-
-bool
-ObjectPlacer::createPlacerMatrix( double lat_deg, double lon_deg, double height, osg::Matrixd& out_result ) const
-{
-    if ( !_mapNode.valid() || !_csn.valid() )
-    {
-        OE_WARN << "ObjectPlacer: terrain is missing either a Map or CSN node" << std::endl;             
-        return false;
-    }
-
-    // see whether this is a geocentric model:
-    bool is_geocentric = _csn.valid() && _csn->getEllipsoidModel() != NULL;
-
-    const SpatialReference* srs = _mapNode->getMap()->getProfile()->getSRS();
-
-    // now build a matrix:
-    if ( is_geocentric )
-    {
-        double lat_rad = osg::DegreesToRadians( lat_deg );
-        double lon_rad = osg::DegreesToRadians( lon_deg );
-
-        if ( _clamp )
-        {
-            osg::Vec3d c;
-            if ( clampGeocentric( _csn.get(), lat_rad, lon_rad, c ) )
-            {
-                srs->getEllipsoid()->computeLocalToWorldTransformFromXYZ( c.x(), c.y(), c.z(), out_result );
-            }
-        }
-        else
-        {
-            srs->getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight( lat_rad, lon_rad, height, out_result );
-        }
-    }
-    else // projected or "flat geographic"
-    {
-        osg::Vec3d local(0, 0, height);
-        
-        // first convert the input coords to the map srs:
-        srs->getGeographicSRS()->transform( osg::Vec3d(lon_deg, lat_deg, height), srs, local );
-        //srs->getGeographicSRS()->transform2D( lon_deg, lat_deg, srs, local.x(), local.y());
-
-        if ( _clamp )
-        {
-            clampProjected( _csn.get(), local.x(), local.y(), local );
-            local.z() += height;
-        }
-        out_result = osg::Matrixd::translate( local );
-    }
-
-    return true;
-}
-
-osg::Node*
-ObjectPlacer::placeNode( osg::Node* node, double lat_deg, double lon_deg, double height ) const
-{
-    osg::Node* result = NULL;
-
-    osg::Matrixd matrix;
-    if ( createPlacerMatrix( lat_deg, lon_deg, height, matrix ) )
-    {
-        osg::MatrixTransform* mt = new osg::MatrixTransform( matrix );
-        mt->addChild( node );
-        result = mt;
-    }
-
-    return result;
-}
diff --git a/src/osgEarthUtil/PolyhedralLineOfSight b/src/osgEarthUtil/PolyhedralLineOfSight
index a1176b7..80d70ed 100644
--- a/src/osgEarthUtil/PolyhedralLineOfSight
+++ b/src/osgEarthUtil/PolyhedralLineOfSight
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -79,6 +79,7 @@ namespace osgEarth { namespace Util
     public: // LocalizedNode
         
         virtual bool setPosition( const GeoPoint& pos );
+        virtual osg::MatrixTransform* getTransform() { return _xform.get(); }
 
     public: // MapNodeObserver
 
@@ -103,6 +104,8 @@ namespace osgEarth { namespace Util
         osg::Geode*            _geode;
         GeoExtent              _extent;
 
+        osg::ref_ptr<osg::MatrixTransform> _xform;
+
         osg::ref_ptr< TerrainCallback > _terrainCallback;
 
         void dirty();
diff --git a/src/osgEarthUtil/PolyhedralLineOfSight.cpp b/src/osgEarthUtil/PolyhedralLineOfSight.cpp
index 7be0409..dd49de4 100644
--- a/src/osgEarthUtil/PolyhedralLineOfSight.cpp
+++ b/src/osgEarthUtil/PolyhedralLineOfSight.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -55,12 +55,14 @@ _distance    ( Distance(50000.0, Units::METERS) )
 {
     OE_WARN << LC << "This class is under development; use at your own risk" << std::endl;
 
+    _xform = new osg::MatrixTransform();
+    this->addChild( _xform.get() );
+
     _geode = new osg::Geode();
     rebuildGeometry();
     recalculateExtent();
 
-    getChildAttachPoint()->addChild( _geode );
-    this->addChild( getRoot() );
+    _xform->addChild( _geode );
 
     _terrainCallback = new TerrainChangedCallback(this);
     
diff --git a/src/osgEarthUtil/RGBColorFilter b/src/osgEarthUtil/RGBColorFilter
index 3932445..c579485 100644
--- a/src/osgEarthUtil/RGBColorFilter
+++ b/src/osgEarthUtil/RGBColorFilter
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/RGBColorFilter.cpp b/src/osgEarthUtil/RGBColorFilter.cpp
index cc53e3f..353d7fe 100644
--- a/src/osgEarthUtil/RGBColorFilter.cpp
+++ b/src/osgEarthUtil/RGBColorFilter.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/RadialLineOfSight b/src/osgEarthUtil/RadialLineOfSight
index ff1cc87..857222b 100644
--- a/src/osgEarthUtil/RadialLineOfSight
+++ b/src/osgEarthUtil/RadialLineOfSight
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/RadialLineOfSight.cpp b/src/osgEarthUtil/RadialLineOfSight.cpp
index 4916932..cf7a2ab 100644
--- a/src/osgEarthUtil/RadialLineOfSight.cpp
+++ b/src/osgEarthUtil/RadialLineOfSight.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ShadowUtils b/src/osgEarthUtil/ShadowUtils
index efc9446..84cb6b2 100644
--- a/src/osgEarthUtil/ShadowUtils
+++ b/src/osgEarthUtil/ShadowUtils
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/ShadowUtils.cpp b/src/osgEarthUtil/ShadowUtils.cpp
index 0dbfcfa..384c24d 100644
--- a/src/osgEarthUtil/ShadowUtils.cpp
+++ b/src/osgEarthUtil/ShadowUtils.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -118,10 +118,10 @@ ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
         }
     }
 
-    // create a virtual program to attach to the shadows scene.
+    // create a virtual program to attach to the shadowed scene.
     VirtualProgram* vp = new VirtualProgram();
     vp->setName( "shadow:terrain" );
-    vp->installDefaultColoringAndLightingShaders();
+    //vp->installDefaultColoringAndLightingShaders();
 
     ssStateSet->setAttributeAndModes( vp, 1 );
 
@@ -137,9 +137,9 @@ ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
         buf << "varying vec4 oe_shadow_TexCoord1;\n";
 
 
-    buf << "void oe_shadow_setupShadowCoords()\n";
+    buf << "void oe_shadow_setupShadowCoords(inout vec4 VertexVIEW)\n";
     buf << "{\n";
-    buf << "    vec4 position4 = gl_ModelViewMatrix * gl_Vertex;\n";
+    buf << "    vec4 position4 = VertexVIEW;\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";
@@ -162,7 +162,7 @@ ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
     vp->setFunction(
         "oe_shadow_setupShadowCoords", 
         setupShadowCoords, 
-        ShaderComp::LOCATION_VERTEX_POST_LIGHTING,
+        ShaderComp::LOCATION_VERTEX_VIEW,
         -1.0 );
 
     std::stringstream buf2;
@@ -207,7 +207,7 @@ ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
     vp->setFunction(
         "oe_shadow_applyLighting",
         fragApplyLighting,
-        osgEarth::ShaderComp::LOCATION_FRAGMENT_POST_LIGHTING );
+        osgEarth::ShaderComp::LOCATION_FRAGMENT_LIGHTING );
 
     setShadowUnit(sscene, su);
 
diff --git a/src/osgEarthUtil/SkyNode b/src/osgEarthUtil/SkyNode
index 86c63ff..f83cd29 100644
--- a/src/osgEarthUtil/SkyNode
+++ b/src/osgEarthUtil/SkyNode
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -114,6 +114,10 @@ namespace osgEarth { namespace Util
         void setAmbientBrightness( float value, osg::View* view =0L );
         float getAmbientBrightness( osg::View* view =0L ) const;
 
+        /** Enables or disables automatic ambience */
+        void setAutoAmbience(bool value);
+        bool getAutoAmbience() const;
+
         /** Whether the moon is visible */
         void setMoonVisible( bool value, osg::View* view =0L );
         bool getMoonVisible( osg::View* view =0L ) const;
@@ -185,6 +189,7 @@ namespace osgEarth { namespace Util
         osg::ref_ptr<osg::Uniform> _starPointSize;
 
         osg::Vec3d _moonPosition;
+        bool _autoAmbience;
 
         osg::ref_ptr< const osg::EllipsoidModel > _ellipsoidModel;
 
diff --git a/src/osgEarthUtil/SkyNode.cpp b/src/osgEarthUtil/SkyNode.cpp
index 37b450d..3f1c447 100644
--- a/src/osgEarthUtil/SkyNode.cpp
+++ b/src/osgEarthUtil/SkyNode.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
@@ -18,14 +18,15 @@
 */
 #include <osgEarthUtil/SkyNode>
 #include <osgEarthUtil/StarData>
+#include <osgEarthUtil/LatLongFormatter>
 
 #include <osgEarth/VirtualProgram>
-#include <osgEarthUtil/LatLongFormatter>
 #include <osgEarth/NodeUtils>
 #include <osgEarth/MapNode>
 #include <osgEarth/Utils>
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
+#include <osgEarth/CullingUtils>
 
 #include <osg/MatrixTransform>
 #include <osg/ShapeDrawable>
@@ -772,6 +773,9 @@ SkyNode::initialize( Map *map, const std::string& starFile )
 
     makeStars(starFile);
 
+    // automatically compute ambient lighting based on the eyepoint
+    _autoAmbience = true;
+
     //Set a default time
     setDateTime( 2011, 3, 6, 18 );
 }
@@ -785,7 +789,7 @@ SkyNode::computeBound() const
 void
 SkyNode::traverse( osg::NodeVisitor& nv )
 {
-    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
+    osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
     if ( cv )
     {
         // If there's a custom projection matrix clamper installed, remove it temporarily.
@@ -797,6 +801,20 @@ SkyNode::traverse( osg::NodeVisitor& nv )
         PerViewDataMap::iterator i = _perViewData.find( view );
         if ( i != _perViewData.end() )
         {
+            if ( _autoAmbience )
+            {
+                const float minAmb = 0.3f;
+                const float maxAmb = 1.0f;
+                const float minDev = -0.2f;
+                const float maxDev = 0.75f;
+                osg::Vec3 eye = cv->getViewPoint(); eye.normalize();
+                osg::Vec3 sun = i->second._lightPos; sun.normalize();
+                float dev = osg::clampBetween(eye*sun, minDev, maxDev);
+                float r   = (dev-minDev)/(maxDev-minDev);
+                float amb = minAmb + r*(maxAmb-minAmb);
+                i->second._light->setAmbient( osg::Vec4(amb,amb,amb,1.0) );
+                //OE_INFO << "dev=" << dev << ", amb=" << amb << std::endl;
+            }
 #if 0
             // adjust the light color based on the eye point and the sun position.
             float aMin =  0.1f;
@@ -942,11 +960,24 @@ SkyNode::getAmbientBrightness( osg::View* view ) const
     return _defaultPerViewData._light->getAmbient().r();
 }
 
+void
+SkyNode::setAutoAmbience( bool value )
+{
+    _autoAmbience = value;
+}
+
+bool
+SkyNode::getAutoAmbience() const
+{
+    return _autoAmbience;
+}
+
 void 
 SkyNode::setAmbientBrightness( PerViewData& data, float value )
 {
     value = osg::clampBetween( value, 0.0f, 1.0f );
     data._light->setAmbient( osg::Vec4f(value, value, value, 1.0f) );
+    _autoAmbience = false;
 }
 
 void
diff --git a/src/osgEarthUtil/SpatialData b/src/osgEarthUtil/SpatialData
index 0ca90fc..e2091e1 100644
--- a/src/osgEarthUtil/SpatialData
+++ b/src/osgEarthUtil/SpatialData
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/SpatialData.cpp b/src/osgEarthUtil/SpatialData.cpp
index 6a45dd6..247b336 100644
--- a/src/osgEarthUtil/SpatialData.cpp
+++ b/src/osgEarthUtil/SpatialData.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
  */
 #include <osgEarthUtil/SpatialData>
 #include <osgEarth/Registry>
-#include <osgUtil/CullVisitor>
+#include <osgEarth/CullingUtils>
 #include <osg/PolygonOffset>
 #include <osg/Polytope>
 #include <osg/Geometry>
@@ -318,7 +318,7 @@ GeoCell::traverse( osg::NodeVisitor& nv )
 
             // custom BSP culling function. this checks that the set of boundary points
             // for this cell intersects the viewing frustum.
-            osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
+            osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
             if ( cv && !intersects( cv->getCurrentCullingSet().getFrustum() ) )
             {
                 return;
diff --git a/src/osgEarthUtil/StarData b/src/osgEarthUtil/StarData
index b599c99..bbc37f7 100644
--- a/src/osgEarthUtil/StarData
+++ b/src/osgEarthUtil/StarData
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TFS b/src/osgEarthUtil/TFS
index a2bfc4d..4564cdc 100644
--- a/src/osgEarthUtil/TFS
+++ b/src/osgEarthUtil/TFS
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TFS.cpp b/src/osgEarthUtil/TFS.cpp
index b901219..c70288e 100644
--- a/src/osgEarthUtil/TFS.cpp
+++ b/src/osgEarthUtil/TFS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TFSPackager b/src/osgEarthUtil/TFSPackager
index d02f3a6..8c3c0b4 100644
--- a/src/osgEarthUtil/TFSPackager
+++ b/src/osgEarthUtil/TFSPackager
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TFSPackager.cpp b/src/osgEarthUtil/TFSPackager.cpp
index 9e116a9..eacd94b 100644
--- a/src/osgEarthUtil/TFSPackager.cpp
+++ b/src/osgEarthUtil/TFSPackager.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TMS b/src/osgEarthUtil/TMS
index 0c4fd05..b804b9b 100644
--- a/src/osgEarthUtil/TMS
+++ b/src/osgEarthUtil/TMS
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TMS.cpp b/src/osgEarthUtil/TMS.cpp
index d2258c7..93bb654 100644
--- a/src/osgEarthUtil/TMS.cpp
+++ b/src/osgEarthUtil/TMS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -266,11 +266,14 @@ TileMap::getURL(const osgEarth::TileKey& tilekey, bool invertY)
         for (TileSetList::iterator itr = _tileSets.begin(); itr != _tileSets.end(); ++itr)
         { 
             if (itr->getOrder() == zoom)
-            {
-                std::stringstream ss;
-                std::string path = osgDB::getFilePath(_filename);
-                ss << path << "/" << zoom << "/" << x << "/" << y << "." << _format.getExtension();
-                //OE_NOTICE << LC << "Returning URL " << ss.str() << std::endl;
+            {                
+                std::stringstream ss; 
+                std::string basePath = osgDB::getFilePath(_filename);                
+                if (!basePath.empty())
+                {
+                    ss << basePath << "/";
+                }
+                ss << zoom << "/" << x << "/" << y << "." << _format.getExtension();                
                 std::string ssStr;
 				ssStr = ss.str();
 				return ssStr;
@@ -279,12 +282,16 @@ TileMap::getURL(const osgEarth::TileKey& tilekey, bool invertY)
     }
     else // Just go with it. No way of knowing the max level.
     {
-        std::stringstream ss;
-        std::string path = osgDB::getFilePath(_filename);
-        ss << path << "/" << zoom << "/" << x << "/" << y << "." << _format.getExtension();
+        std::stringstream ss; 
+        std::string basePath = osgDB::getFilePath(_filename);                
+        if (!basePath.empty())
+        {
+            ss << basePath << "/";
+        }
+        ss << zoom << "/" << x << "/" << y << "." << _format.getExtension();                
         std::string ssStr;
-		ssStr = ss.str();
-		return ssStr;        
+        ssStr = ss.str();
+        return ssStr;
     }
 
     return "";
diff --git a/src/osgEarthUtil/TMSBackFiller b/src/osgEarthUtil/TMSBackFiller
new file mode 100644
index 0000000..5d7c08b
--- /dev/null
+++ b/src/osgEarthUtil/TMSBackFiller
@@ -0,0 +1,94 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 OSGEARTHUTIL_TMS_BACKFILLER_H
+#define OSGEARTHUTIL_TMS_BACKFILLER_H
+
+#include <osgEarthUtil/Common>
+#include <osgEarth/Profile>
+
+#include <osgEarthUtil/TMS>
+
+namespace osgEarth { namespace Util
+{
+    using namespace osgEarth::Util::TMS;
+
+    /**
+     * Post processing utility for disk based TMS (Tile Map Service) repositories that takes data from a given level and "backfills" lower
+     * levels of data by mosaciing and resampling the higher lod data.  This process is useful when processing web datasets that switch from one
+     * dataset to another at distinct lods which looks fine when viewed in a 2D slippy map but look incorrect when viewed at an angle in 3D
+     * in views that contain neighboring lods.
+     */
+    class OSGEARTHUTIL_EXPORT TMSBackFiller
+    {
+    public:
+        TMSBackFiller();
+
+        /**
+        * Whether to dump out progress messages 
+        * default = false
+        */
+        void setVerbose( bool value ) { _verbose = value; }
+        bool getVerbose() const { return _verbose; }
+
+        /**
+        * The level to backfill up to
+        */
+        void setMinLevel( unsigned int value ) { _minLevel = value; }
+        unsigned int getMinLevel() const { return _minLevel; }
+
+        /**
+        * The level to start backfilling from.  All tiles up to the min level will be regenerated using tiles from this level of detail.
+        */
+        void setMaxLevel( unsigned int value ) { _maxLevel = value; }
+        unsigned int getMaxLevel() const { return _maxLevel; }
+
+        /**
+        * The bounds to backfill within
+        */
+        const Bounds& getBounds() const { return _bounds;}
+        void setBounds( Bounds& bounds) { _bounds = bounds;}
+
+        /**
+         * Processes the given TMS file with the given options
+         */
+        void process( const std::string& tms, osgDB::Options* options );                        
+
+    private:
+
+        void processKey( const TileKey& key );
+
+        std::string getFilename( const TileKey& key );
+        
+        osg::Image* readTile( const TileKey& key );
+
+        void writeTile( const TileKey& key, osg::Image* image );
+        
+        osg::ref_ptr< TileMap > _tileMap;
+
+        unsigned int _minLevel;
+        unsigned int _maxLevel;
+        bool _verbose;
+        std::string _tmsPath;
+        Bounds _bounds;
+        osg::ref_ptr< osgDB::Options > _options;
+    };
+
+} } // namespace osgEarth::Util
+
+#endif // OSGEARTHUTIL_TMS_PACKAGER_H
diff --git a/src/osgEarthUtil/TMSBackFiller.cpp b/src/osgEarthUtil/TMSBackFiller.cpp
new file mode 100644
index 0000000..b3fbece
--- /dev/null
+++ b/src/osgEarthUtil/TMSBackFiller.cpp
@@ -0,0 +1,140 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2008-2013 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 <osgEarthUtil/TMSBackFiller>
+#include <osgEarth/ImageUtils>
+#include <osgEarth/FileUtils>
+#include <osgEarth/ImageMosaic>
+
+#include <osgDB/FileUtils>
+#include <osgDB/FileNameUtils>
+#include <osgDB/ReadFile>
+#include <osgDB/WriteFile>
+
+#define LC "[TMSBackFiller] "
+
+using namespace osgEarth::Util;
+using namespace osgEarth;
+
+TMSBackFiller::TMSBackFiller()
+{
+}
+
+
+void TMSBackFiller::process( const std::string& tms, osgDB::Options* options )
+{               
+    std::string fullPath = getFullPath( "", tms );        
+    _options = options;
+
+    //Read the tilemap
+    _tileMap = TileMapReaderWriter::read( fullPath, 0 );
+    if (_tileMap)
+    {                        
+        //The max level is where we are going to read data from, so we need to start one level up.
+        osg::ref_ptr< const Profile> profile = _tileMap->createProfile();           
+
+        //If the bounds aren't valid just use the full extent of the profile.
+        if (!_bounds.valid())
+        {                
+            _bounds = profile->getExtent().bounds();
+        }
+
+
+        int firstLevel = _maxLevel-1;            
+
+        GeoExtent extent( profile->getSRS(), _bounds );           
+
+        //Process each level in it's entirety
+        for (int level = firstLevel; level >= static_cast<int>(_minLevel); level--)
+        {
+            if (_verbose) OE_NOTICE << "Processing level " << level << std::endl;                
+
+            TileKey ll = profile->createTileKey(extent.xMin(), extent.yMin(), level);
+            TileKey ur = profile->createTileKey(extent.xMax(), extent.yMax(), level);
+
+            for (unsigned int x = ll.getTileX(); x <= ur.getTileX(); x++)
+            {
+                for (unsigned int y = ur.getTileY(); y <= ll.getTileY(); y++)
+                {
+                    TileKey key = TileKey(level, x, y, profile.get());
+                    processKey( key );
+                }
+            }                
+
+        }            
+    }
+    else
+    {
+        OE_NOTICE << "Failed to load TileMap from " << _tmsPath << std::endl;
+    }
+}
+
+void TMSBackFiller::processKey( const TileKey& key )
+{
+    if (_verbose) OE_NOTICE << "Processing key " << key.str() << std::endl;
+
+    //Get all of the child tiles for this key, load them and mosaic them into a new tile
+    TileKey ulKey = key.createChildKey( 0 );
+    TileKey urKey = key.createChildKey( 1 );
+    TileKey llKey = key.createChildKey( 2 );
+    TileKey lrKey = key.createChildKey( 3 );
+
+    osg::ref_ptr< osg::Image > ul = readTile( ulKey );
+    osg::ref_ptr< osg::Image > ur = readTile( urKey );
+    osg::ref_ptr< osg::Image > ll = readTile( llKey );
+    osg::ref_ptr< osg::Image > lr = readTile( lrKey );
+
+    if (ul.valid() && ur.valid() && ll.valid() && lr.valid())
+    {            
+        //Merge them together
+        ImageMosaic mosaic;
+        mosaic.getImages().push_back( TileImage( ul.get(), ulKey ) );
+        mosaic.getImages().push_back( TileImage( ur.get(), urKey ) );
+        mosaic.getImages().push_back( TileImage( ll.get(), llKey ) );
+        mosaic.getImages().push_back( TileImage( lr.get(), lrKey ) );            
+
+        osg::ref_ptr< osg::Image> merged = mosaic.createImage();
+        if (merged.valid())
+        {
+            //Resize the image so it's the same size as one of the input files
+            osg::ref_ptr<osg::Image> resized;
+            ImageUtils::resizeImage( merged.get(), ul->s(), ul->t(), resized );
+            std::string outputFilename = getFilename( key );                
+            writeTile( key, resized.get() );
+        }
+    }                
+}    
+
+std::string TMSBackFiller::getFilename( const TileKey& key )
+{
+    return _tileMap->getURL( key, false );        
+}
+
+osg::Image* TMSBackFiller::readTile( const TileKey& key )
+{
+    std::string filename = getFilename( key );        
+    return osgDB::readImageFile( filename );        
+}
+
+void TMSBackFiller::writeTile( const TileKey& key, osg::Image* image )
+{
+    std::string filename = getFilename( key );
+    if ( !osgDB::fileExists( osgDB::getFilePath(filename) ) )
+        osgDB::makeDirectoryForFile( filename );
+    osgDB::writeImageFile( *image, filename, _options.get() );        
+}     
\ No newline at end of file
diff --git a/src/osgEarthUtil/TMSPackager b/src/osgEarthUtil/TMSPackager
index e58ebfc..0493605 100644
--- a/src/osgEarthUtil/TMSPackager
+++ b/src/osgEarthUtil/TMSPackager
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TMSPackager.cpp b/src/osgEarthUtil/TMSPackager.cpp
index 2907262..ceebf77 100644
--- a/src/osgEarthUtil/TMSPackager.cpp
+++ b/src/osgEarthUtil/TMSPackager.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TerrainProfile b/src/osgEarthUtil/TerrainProfile
index ffda042..419a4fe 100644
--- a/src/osgEarthUtil/TerrainProfile
+++ b/src/osgEarthUtil/TerrainProfile
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/TerrainProfile.cpp b/src/osgEarthUtil/TerrainProfile.cpp
index ceaa2f1..48f4458 100644
--- a/src/osgEarthUtil/TerrainProfile.cpp
+++ b/src/osgEarthUtil/TerrainProfile.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/UTMGraticule b/src/osgEarthUtil/UTMGraticule
index 3a7bc96..0c287b0 100644
--- a/src/osgEarthUtil/UTMGraticule
+++ b/src/osgEarthUtil/UTMGraticule
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/UTMGraticule.cpp b/src/osgEarthUtil/UTMGraticule.cpp
index bd12aba..3bc7e5d 100644
--- a/src/osgEarthUtil/UTMGraticule.cpp
+++ b/src/osgEarthUtil/UTMGraticule.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
@@ -185,6 +185,7 @@ UTMGraticule::rebuild()
     _root = new DrapeableNode( getMapNode(), false );
     this->addChild( _root );
 
+#if 0
     // set up depth offsetting.
     osg::StateSet* s = _root->getOrCreateStateSet();
     s->setAttributeAndModes( DepthOffsetUtils::getOrCreateProgram(), 1 );
@@ -192,6 +193,7 @@ UTMGraticule::rebuild()
     osg::Uniform* u = DepthOffsetUtils::createMinOffsetUniform();
     u->set( 10000.0f );
     s->addUniform( u );
+#endif
 
     // build the base Grid Zone Designator (GZD) loolup table. This is a table
     // that maps the GZD string to its extent.
@@ -301,15 +303,20 @@ UTMGraticule::buildGZDTile( const std::string& name, const GeoExtent& extent )
     // get the geocentric tile center:
     osg::Vec3d tileCenter;
     extent.getCentroid( tileCenter.x(), tileCenter.y() );
+
+    const SpatialReference* ecefSRS = extent.getSRS()->getECEF();
     
     osg::Vec3d centerECEF;
-    extent.getSRS()->transformToECEF( tileCenter, centerECEF );
+    extent.getSRS()->transform( tileCenter, ecefSRS, centerECEF );
+    //extent.getSRS()->transformToECEF( tileCenter, centerECEF );
 
     if ( hasText )
     {
         osg::Vec3d west, east;
-        extent.getSRS()->transformToECEF(osg::Vec3d(extent.xMin(),tileCenter.y(),0), west );
-        extent.getSRS()->transformToECEF(osg::Vec3d(extent.xMax(),tileCenter.y(),0), east );
+        extent.getSRS()->transform( osg::Vec3d(extent.xMin(),tileCenter.y(),0), ecefSRS, west );
+        extent.getSRS()->transform( osg::Vec3d(extent.xMax(),tileCenter.y(),0), ecefSRS, east );
+        //extent.getSRS()->transformToECEF(osg::Vec3d(extent.xMin(),tileCenter.y(),0), west );
+        //extent.getSRS()->transformToECEF(osg::Vec3d(extent.xMax(),tileCenter.y(),0), east );
 
         TextSymbol* textSym = _options->primaryStyle()->getOrCreate<TextSymbol>();
         textSym->size() = (west-east).length() / 3.0;
@@ -324,7 +331,9 @@ UTMGraticule::buildGZDTile( const std::string& name, const GeoExtent& extent )
         d->getOrCreateStateSet()->setRenderBinToInherit();
 
         textGeode->addDrawable(d);
-        osg::MatrixTransform* mt = new osg::MatrixTransform(ECEF::createLocalToWorld(centerECEF));
+        osg::Matrixd centerL2W;
+        ecefSRS->createLocalToWorld( centerECEF, centerL2W );
+        osg::MatrixTransform* mt = new osg::MatrixTransform(centerL2W);
         mt->addChild(textGeode);
        
         group->addChild(mt);
diff --git a/src/osgEarthUtil/WFS b/src/osgEarthUtil/WFS
index 793d1de..8e628f0 100644
--- a/src/osgEarthUtil/WFS
+++ b/src/osgEarthUtil/WFS
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/WFS.cpp b/src/osgEarthUtil/WFS.cpp
index 3f3b5e0..bc78ecb 100644
--- a/src/osgEarthUtil/WFS.cpp
+++ b/src/osgEarthUtil/WFS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/WMS b/src/osgEarthUtil/WMS
index dcbfb21..ffa166d 100644
--- a/src/osgEarthUtil/WMS
+++ b/src/osgEarthUtil/WMS
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
+* Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
diff --git a/src/osgEarthUtil/WMS.cpp b/src/osgEarthUtil/WMS.cpp
index bf18ff4..5d7bbbd 100644
--- a/src/osgEarthUtil/WMS.cpp
+++ b/src/osgEarthUtil/WMS.cpp
@@ -1,6 +1,6 @@
 /* -*-c++-*- */
 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2008-2012 Pelican Mapping
+ * Copyright 2008-2013 Pelican Mapping
  * http://osgearth.org
  *
  * osgEarth is free software; you can redistribute it and/or modify
diff --git a/tests/annotation.earth b/tests/annotation.earth
index ae63859..bb89532 100644
--- a/tests/annotation.earth
+++ b/tests/annotation.earth
@@ -20,48 +20,72 @@ osgEarth Sample - Annotations
     </options>
     
     <external>
-        <viewpoint name="Annotation Samples" heading="35.27" lat="33" long="-118" pitch="-35" range="500000"/>
+        <viewpoints>
+            <viewpoint name="Annotation Samples" 
+                       lat="33" long="-118" range="500000"
+                       heading="35.27" pitch="-35" />
+        </viewpoints>
         
-        <annotations declutter="true">
+        <annotations>
         
             <label text="Los Angeles">
                 <position lat="34.051" long="-117.974" alt="100" mode="relative"/>
                 <style type="text/css">
-                    text-align: center_center;
-                    text-size:  20;
+                    text-align:     center_center;
+                    text-size:      20;
+                    text-declutter: true;
                 </style>
             </label>
             
             <place text="San Diego">
                 <position lat="32.73" long="-117.17"/>
                 <icon>http://demo.pelicanmapping.com/icons/gmaps/yoga.png</icon>
+                <style type="text/css">
+                    text-declutter: true;
+                </style>
             </place>
             
-            <circle draped="true">
+            <circle name="draped circle">
                 <position lat="34.051" long="-117.974"/>
                 <radius value="50" units="km"/>
-                <style type="text/css">fill: #ffff0080;</style>
+                <style type="text/css">
+                    fill:               #ffff0080;
+                    altitude-clamping:  terrain;
+                    altitude-technique: drape;
+                </style>
             </circle>
             
-            <ellipse>
+            <ellipse name="ellipse relative">
+                <position lat="34.051" long="-116" hat="5000"/>
+                <radius_major value="50" units="km"/>
+                <radius_minor value="40" units="km"/>
+                <style type="text/css">
+                    fill: #ffff0080;
+                </style>
+            </ellipse>
+            
+            <ellipse name="ellipse extruded">
                 <position lat="32.73" long="-119.0"/>
                 <radius_major value="50" units="km"/>
                 <radius_minor value="20" units="km"/>
                 <style type="text/css">
-                    fill:             #ff7f008f;
+                    fill:             #ff7f007f;
                     stroke:           #ff0000ff;
                     extrusion-height: 5000;
                 </style>
             </ellipse>
             
-            <rectangle>
+            <rectangle name="absolute rectangle">
                 <position lat="32.2" long="-118" alt="1000"/>
                 <width value="50" units="nm"/>
                 <height value="25" units="nm"/>
-                <style type="text/css"> stroke: #00ffff; stroke-width: 2; </style>
+                <style type="text/css">
+                    stroke:       #00ffff; 
+                    stroke-width: 2; 
+                </style>
             </rectangle>
 
-            <feature>
+            <feature name="extruded line">
                 <srs>wgs84</srs>
                 <geometry>
                     LINESTRING(-120.37 34.039, -120.09 33.96, -119.75 34, -118.43 33.37, -118.48 32.88)
@@ -71,23 +95,26 @@ osgEarth Sample - Annotations
                     stroke:           #ffff00;
                     stroke-width:     3;
                     extrusion-height: 30000;
+                    render-lighting:  false;
                 </style>
-                <lighting>false</lighting>
             </feature>
-            
-            <local_geometry>
+
+            <feature name="gpu clamped line">
+                <srs>wgs84</srs>
                 <geometry>
-                    POLYGON((0 0 0, -1000 0 25000, -5000 0 25000, 0 0 30000, 5000 0 25000, 1000 0 25000))
+                    LINESTRING(-120 37, -120 33, -118 33, -118 32)
                 </geometry>
-                <position lat="33.4" long="-116.6" alt="0"/>
                 <style type="text/css">
-                    fill: #00ff00;
-                    stroke: #ffff00;
+                    stroke:              #ff3000;
+                    stroke-width:        3;
+                    stroke-tessellation: 100;
+                    altitude-clamping:   terrain;
+                    altitude-technique:  gpu;
+                    render-lighting:     false;
                 </style>
-                <lighting>false</lighting>
-            </local_geometry>
+            </feature>
             
-            <model>
+            <model name="flag model">
                 <url>../data/red_flag.osg.18000.scale</url>
                 <position lat="33" long="-117.75" hat="0"/>
             </model>
@@ -102,6 +129,27 @@ osgEarth Sample - Annotations
                 <position lat="26" long="-81" />
             </label>
             
+            <local_geometry name="3D geometry">
+                <geometry>
+                    POLYGON((0 0 0, -1000 0 25000, -5000 0 25000, 0 0 30000, 5000 0 25000, 1000 0 25000))
+                </geometry>
+                <position lat="33.4" long="-116.6" hat="0"/>
+                <style type="text/css">
+                    fill:            #00ff00;
+                    stroke:          #ffff00;
+                    render-lighting: false;
+                </style>
+            </local_geometry>
+            
+            <circle name="scene-clamped circle">
+                <position lat="33.4" long="-116.6"/>
+                <radius value="5" units="km"/>
+                <style type="text/css">
+                    fill: #afafff9f;
+                    render-lighting: false;
+                </style>
+            </circle>
+            
         </annotations>
         
     </external>
diff --git a/tests/boston.earth b/tests/boston.earth
index 7e71775..5a24ddf 100644
--- a/tests/boston.earth
+++ b/tests/boston.earth
@@ -7,9 +7,30 @@ to extruded buildings.
 
 <map name="Boston Demo" type="geocentric" version="2">
     
-    <image name="ReadyMap.org - Imagery" driver="tms">
+    <options>
+        <terrain sample_ratio="0.25" lighting="true"/>
+    </options>
+    
+    
+    <image name="mapquest_open_aerial" driver="xyz" enabled="false">
+        <url>http://oatile[1234].mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg</url>
+        <profile>spherical-mercator</profile>
+        <cache_policy usage="no_cache"/>
+        <nodata_image>http://oatile3.mqcdn.com/tiles/1.0.0/sat/13/636/6210.jpg</nodata_image>
+    </image>
+    
+    <image name="readymap_imagery" driver="tms">
         <url>http://readymap.org/readymap/tiles/1.0.0/22/</url>
+        <color_filters>
+            <gamma rgb="1.3"/>
+        </color_filters>
     </image>
+    
+        
+    <elevation name="readymap_elevation" driver="tms">
+        <url>http://readymap.org/readymap/tiles/1.0.0/9/</url>
+    </elevation>
+    
       
     <model name="buildings" driver="feature_geom">
              
@@ -18,9 +39,14 @@ to extruded buildings.
             <build_spatial_index>true</build_spatial_index>
         </features>
         
-        <feature_indexing>true</feature_indexing>
+        <feature_indexing/>
         
-        <fade_in_duration>1.0</fade_in_duration>
+        <!--
+        <fading
+            duration="1.0"
+            max_range="20000"
+            attenuation_distance="2000"/>
+            -->
         
         <!--
            The "layout" element activates tiling and paging of the feature set. If you
@@ -39,11 +65,8 @@ to extruded buildings.
               range to decide how many tiles to create.)
         -->
         
-        <layout>
-            <tile_size_factor>45</tile_size_factor>
-            <level name="default" max_range="20000">
-                <selector class="buildings"/>
-            </level>
+        <layout tile_size_factor="52">
+            <level name="default" max_range="20000" style="buildings"/>
         </layout>
         
         <styles>            
@@ -53,12 +76,15 @@ to extruded buildings.
             
             <style type="text/css">
                 buildings {
-                    extrusion-height:      3.5 * max([story_ht_], 1);
-                    extrusion-flatten:     true;
-                    extrusion-wall-style:  building-wall;
+                    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;
+                    extrusion-roof-style:    building-rooftop;
+                    altitude-clamping:       terrain;
+                    altitude-technique:      map;
+                    altitude-binding:        vertex;
+                    //altitude-resolution:     0.001;
                 }            
                 building-wall {
                     skin-library:     us_resources;
@@ -77,6 +103,62 @@ to extruded buildings.
         </styles>   
     </model>
     
+    
+    <model name="Streets" driver="feature_geom" enabled="true">
+        <features name="streets" driver="ogr" build_spatial_index="true">
+            <url>../data/boston-scl-utm19n-meters.shp</url>
+            <resample max_length="25"/>
+        </features>
+        
+        <!--
+        <fading max_range="3500"/>
+        -->
+        
+        <layout crop_features="true" tile_size_factor="7.5">
+            <level max_range="5000"/>
+        </layout>
+        
+        <styles>
+            <style type="text/css">
+                streets {
+                    stroke:                       #ffff007f;
+                    stroke-width:                 7.5m;
+                    altitude-clamping:            terrain;
+                    altitude-technique:           gpu;
+                    render-depth-offset-min-bias: 10;
+                }
+            </style>
+        </styles>        
+    </model>
+    
+    
+    <model name="Parks" driver="feature_geom" enabled="true">
+        <features name="parks" driver="ogr" build_spatial_index="true">
+            <url>../data/boston-parks.shp</url>
+        </features>
+        
+        <layout tile_size_factor="3.5">
+            <level max_range="3500"/>
+        </layout>
+        
+        <instancing>true</instancing>
+        
+        <styles>
+            <style type="text/css">
+                parks {
+                   model:                  "../data/tree.ive";
+                   model-placement:        random;
+                   model-density:          4000;
+                   model-scale:            1.0;
+                   altitude-clamping:      terrain;
+                   altitude-technique:     map;
+                   altitude-resolution:    0.001;
+                }
+            </style>
+        </styles>        
+    </model>
+    
+    
     <external>
         <viewpoints>
             <viewpoint name="Boston Overview" heading="24.261" height="0" lat="42.34425" long="-71.076262" pitch="-21.6" range="3450"/>
@@ -84,7 +166,7 @@ to extruded buildings.
             <viewpoint name="Boston Downtown 2" heading="-128.5" lat="42.3582" long="-71.0546" height="0" pitch="-19" range="1620" />
             <viewpoint name="Boston Street Level" heading="-145.85" lat="42.36460" long="-71.053612" pitch="-10.1" range="85.034"/>
         </viewpoints>
-        <sky hours="21.0"/>
+        <sky hours="14.0"/>
     </external>
   
 </map>
diff --git a/tests/byo.earth b/tests/byo.earth
new file mode 100644
index 0000000..44f0a8c
--- /dev/null
+++ b/tests/byo.earth
@@ -0,0 +1,12 @@
+<!--
+osgEarth Sample - "Bring Your Own" Terrain Engine.
+-->
+<map name="byo" type="geocentric" version="2">
+
+	<options>
+		<terrain driver="byo">
+			<url>http://www.openscenegraph.org/data/earth_bayarea/earth.ive</url>
+		</terrain>
+	</options>
+    
+</map>
diff --git a/tests/feature_multilod.earth b/tests/feature_multilod.earth
new file mode 100644
index 0000000..16f9fb3
--- /dev/null
+++ b/tests/feature_multilod.earth
@@ -0,0 +1,62 @@
+<!--
+osgEarth Sample
+Shows how to create a multi-LOD setup for GPU-clamped geometry.
+-->
+
+<map name="Geometry Rasterizer Demo" type="round" version="2">
+  
+    <image name="readymap_imagery" driver="tms">
+        <url>http://readymap.org/readymap/tiles/1.0.0/7/</url>
+        <color_filters>
+            <gamma rgb="1.3"/>
+        </color_filters>
+    </image>
+        
+    <elevation name="readymap_elevation" driver="tms">
+        <url>http://readymap.org/readymap/tiles/1.0.0/9/</url>
+    </elevation>
+    
+    <options lighting="false">
+      <terrain normalize_edges="true"/>
+    </options>
+    
+    
+    <model name="roads" driver="feature_geom" lighting="false">
+
+        <features name="roads" driver="ogr">
+            <url>../data/istates_dissolve.shp</url>
+            <ogr_driver>ESRI Shapefile</ogr_driver>
+            <build_spatial_index>true</build_spatial_index>
+        </features>
+
+        <layout crop_features="true">
+            <level style="lo"  min_range="250000"/>
+            <level style="mid" max_range="350000" min_range="25000"/>
+            <level style="hi"  max_range="75000"/>
+        </layout>
+        
+        <styles>
+            <style type="text/css">
+                lo {
+                    stroke:              #ffff009f;
+                    stroke-width:        2;
+                    altitude-clamping:   terrain-gpu;
+                }           
+                mid {
+                    stroke:              #ffff009f;
+                    stroke-width:        3;
+                    altitude-clamping:   terrain-gpu;
+                }                
+                hi {
+                    stroke:              #ffff009f;
+                    stroke-width:        125m;
+                    stroke-tessellation: 5;
+                    altitude-clamping:   terrain-gpu;
+                    render-depth-offset-min-bias: 20;
+                }        
+            </style>
+        </styles>
+        
+    </model>
+  
+</map>
diff --git a/tests/feature_overlay.earth b/tests/feature_overlay.earth
index 9d36c28..ff2de35 100644
--- a/tests/feature_overlay.earth
+++ b/tests/feature_overlay.earth
@@ -1,6 +1,7 @@
 <!--
 osgEarth Sample
-Demonstrates use of the "feature_overlay" feature draping driver.
+
+Demonstrate the Altitude Symbol and GPU-based vertex clamping.
 -->
 
 <map name="Geometry Rasterizer Demo" type="round" version="2">
@@ -13,25 +14,25 @@ Demonstrates use of the "feature_overlay" feature draping driver.
         <url>../data/world.tif</url>
     </image>
     
-    <overlay name="world_boundaries" driver="feature_geom">
+    <model name="world_boundaries" driver="feature_geom">
 
         <!-- Configure the OGR feature driver to read the shapefile. -->             
         <features name="world" driver="ogr">
             <url>../data/world.shp</url>
             <build_spatial_index>true</build_spatial_index>
-            <convert type="line"/>
         </features>
                 
         <styles>
             <style type="text/css">
                 world {
-                   stroke: #ffff00;
-                   stroke-opacity: 1.0;
-                   stroke-width: 3.0;
+                   stroke:             #ffff00;
+                   stroke-opacity:     1.0;
+                   stroke-width:       3.0;
+                   altitude-clamping:  terrain-gpu;
                 }            
             </style>
         </styles>
         
-    </overlay>
+    </model>
   
 </map>
diff --git a/tests/feature_overlay_polys.earth b/tests/feature_overlay_polys.earth
index c49208f..663d968 100644
--- a/tests/feature_overlay_polys.earth
+++ b/tests/feature_overlay_polys.earth
@@ -16,7 +16,7 @@ on the map using the overlay technique.
         <url>../data/world.tif</url>
     </image>
     
-    <model name="countries" driver="feature_geom" overlay="true">
+    <model name="countries" driver="feature_geom">
                           
         <features name="states" driver="ogr">
             <url>../data/world.shp</url>
@@ -27,24 +27,20 @@ on the map using the overlay technique.
         
             <style type="text/css">
                 p1 {
-                   fill: #ffff80;
-                   fill-opacity: 0.4;
+                   fill:               #ffff8066;
+                   altitude-clamping:  terrain-drape;
                 }       
                 p2 {
-                   fill: #80ffff;
-                   fill-opacity: 0.4;
+                   fill:               #80ffff66;
                 }   
                 p3 {
-                   fill: #ff80ff;
-                   fill-opacity: 0.4;
+                   fill:               #ff80ff66;
                 }       
                 p4 {
-                   fill: #ff8080;
-                   fill-opacity: 0.4;
+                   fill:               #ff808066;
                 }     
                 p5 {
-                   fill: #80ff80;
-                   fill-opacity: 0.4;
+                   fill:               #80ff8066;
                 }                                      
             </style>
         
diff --git a/tests/feature_rasterize.earth b/tests/feature_rasterize.earth
index 3a833a6..5fe3be6 100644
--- a/tests/feature_rasterize.earth
+++ b/tests/feature_rasterize.earth
@@ -18,24 +18,24 @@ Demonstrates use of the "agglite" feature rasterization driver.
             <url>../data/world.shp</url>
             <build_spatial_index>true</build_spatial_index>
         </features>
-        
-        <!-- Treat the geometry as lines. -->
-        <geometry_type>line</geometry_type>
-                
-        <!-- This means the "stroke-width" in the style is (approximately) in pixels. -->
-        <relative_line_size>true</relative_line_size>
-        
-        <lod_blending>true</lod_blending>
-                
+		
         <styles>
+			<selector class="outline"/>
+			<selector class="line"/>
+		
             <style type="text/css">
-                world {
-                   stroke: #ffffff;
-                   stroke-opacity: 1.0;
-                   stroke-width: 0.75;
+				outline {
+					stroke:		   #ffffff;
+					stroke-width:  5px;
+				}
+                line {
+                   stroke:         #ff0000;
+                   stroke-width:   2.5px;
                 }            
             </style>
         </styles>
+		
+		<cache_policy usage="no_cache"/>
         
     </image>
   
diff --git a/tests/feature_rasterize_2.earth b/tests/feature_rasterize_2.earth
index d4f8263..faefc6f 100644
--- a/tests/feature_rasterize_2.earth
+++ b/tests/feature_rasterize_2.earth
@@ -11,11 +11,16 @@ rasterizing it into image tiles.
         <lighting>false</lighting>
     </options>
     
-    <image name="world" driver="gdal">
-        <url>../data/world.tif</url>
+    <image name="mapquest_open_aerial" driver="xyz">
+        <url>http://oatile[1234].mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg</url>
+        <profile>spherical-mercator</profile>
+        <cache_policy usage="no_cache"/>
+        <nodata_image>http://oatile3.mqcdn.com/tiles/1.0.0/sat/13/636/6210.jpg</nodata_image>
     </image>
     
     <image name="states" driver="agglite">
+	
+		<cache_policy usage="no_cache"/>
     
         <features name="states" driver="ogr">
             <url>../data/usa.shp</url>
@@ -26,8 +31,8 @@ rasterizing it into image tiles.
         <styles>            
             <style type="text/css">
                 default {
-                    fill: #ffffff;
-                    fill-opacity: 0.5;
+                    stroke:       #ffff00;
+					stroke-width: .2km;
                 }            
             </style>              
         </styles>
@@ -35,6 +40,8 @@ rasterizing it into image tiles.
     </image>
     
     <image name="roads" driver="agglite" min_level="3">
+	
+		<cache_policy usage="no_cache"/>
     
         <features name="roads" driver="ogr">
             <url>../data/istates_dissolve.shp</url>
@@ -42,13 +49,12 @@ rasterizing it into image tiles.
             <build_spatial_index>true</build_spatial_index>
         </features>
         
-
         <styles>
             <style type="text/css">
                 default {
-                    stroke: #ff6666;
-                    stroke-width: 0.75;
-                    stroke-opacity: 1.0;
+                    stroke:            #ff6666ff;
+                    stroke-width:      2km;
+					stroke-min-pixels: 2;
                 }            
             </style>
         </styles>
diff --git a/tests/readymap.earth b/tests/glsl_filter.earth
similarity index 77%
copy from tests/readymap.earth
copy to tests/glsl_filter.earth
index 627c18c..6f8b65d 100644
--- a/tests/readymap.earth
+++ b/tests/glsl_filter.earth
@@ -15,6 +15,11 @@ http://readymap.org
 
     <image name="readymap_imagery" driver="tms">
         <url>http://readymap.org/readymap/tiles/1.0.0/7/</url>
+        <color_filters>
+			<glsl name="gamma correction">
+				color.rgb = pow(color.rgb, 1.0/vec3(1.3));
+			</glsl>
+        </color_filters>
     </image>
         
     <elevation name="readymap_elevation" driver="tms">
@@ -22,7 +27,8 @@ http://readymap.org
     </elevation>
     
     <options>
-      <terrain normalize_edges="true"/>
+		<elevation_tile_size>8</elevation_tile_size>
+        <terrain normalize_edges="true" first_lod="2"/>
     </options>
     
 </map>
diff --git a/tests/ocean.earth b/tests/ocean.earth
index 6509a01..1f8a3ca 100644
--- a/tests/ocean.earth
+++ b/tests/ocean.earth
@@ -38,10 +38,24 @@ http://readymap.org
     
     <external>
         <sky/>
+        
+        <!-- Ocean parameters. -->
         <ocean>
+        
+            <!-- Surface texture to use -->
             <texture_url>../data/watersurface1.png</texture_url>
-            <base_color>#334f7faf</base_color>
+            
+            <!-- Masking layer to use (optional...without a masking layer, osgEarth will sample the
+                 terrain elevation data to determine where the ocean is.
+            <mask_layer driver="tms">
+                <url>http://readymap.org/readymap/tiles/1.0.0/2/</url>
+            </mask_layer>
+            -->
+            
+            <!-- surface color (before texturing) -->
+            <base_color>#334f7fbf</base_color>
         </ocean>
+        
         <viewpoint name="Los Angeles" heading="35.27" height="97.48" lat="34.051" long="-117.974" pitch="-17" range="136405"/>
     </external>
 </map>
diff --git a/tests/openstreetmap_flat.earth b/tests/openstreetmap_flat.earth
index b394afd..ea57710 100644
--- a/tests/openstreetmap_flat.earth
+++ b/tests/openstreetmap_flat.earth
@@ -9,6 +9,7 @@ http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
     <options>
         <profile>spherical-mercator</profile>
         <lighting>false</lighting>
+        <terrain engine="quadtree" range_mode="PIXEL_SIZE_ON_SCREEN" tile_pixel_size="256"/>
     </options>
     
     <image name="osm_mapnik" driver="xyz">
diff --git a/tests/readymap.earth b/tests/readymap.earth
index 627c18c..82bdc7b 100644
--- a/tests/readymap.earth
+++ b/tests/readymap.earth
@@ -15,6 +15,9 @@ http://readymap.org
 
     <image name="readymap_imagery" driver="tms">
         <url>http://readymap.org/readymap/tiles/1.0.0/7/</url>
+        <color_filters>
+            <gamma rgb="1.3"/>
+        </color_filters>
     </image>
         
     <elevation name="readymap_elevation" driver="tms">
@@ -22,7 +25,9 @@ http://readymap.org
     </elevation>
     
     <options>
-      <terrain normalize_edges="true"/>
+		<lighting>false</lighting>
+		<elevation_tile_size>8</elevation_tile_size>
+        <terrain normalize_edges="true" first_lod="1"/>
     </options>
     
 </map>
diff --git a/tests/wms-t_nexrad_animated.earth b/tests/wms-t_nexrad_animated.earth
index 880d4db..15c3d58 100644
--- a/tests/wms-t_nexrad_animated.earth
+++ b/tests/wms-t_nexrad_animated.earth
@@ -10,7 +10,7 @@ US NEXRAD 45 minute radar returns overlaid on imagery.
         <url>../data/world.tif</url>
     </image>
     
-    <image name="nexrad45min" driver="wms" loading_weight="10">
+    <image name="nexrad45min" driver="wms">
         <url>http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi</url>
         <format>png</format>
         <layers>nexrad-n0r-wmst</layers>
@@ -29,6 +29,7 @@ US NEXRAD 45 minute radar returns overlaid on imagery.
             2005-08-29T20:00:00Z
         </times>
         <seconds_per_frame>0.25</seconds_per_frame>
+        <cache_policy usage="no_cache"/>
     </image>
     
     <options>

-- 
osgEarth terrain rendering toolkit



More information about the Pkg-grass-devel mailing list